Получить номер строки из байтового смещения

12

Наличие байтового смещения для файла.

Есть ли инструмент, который дает номер строки для этого байта?

  • Количество байтов, начинающееся с нуля, как в: первый байт 0, а не 1.
  • Номер строки начинается с 1.
  • Файл может иметь как простой текст, так и двоичные двоичные объекты, многобайтовые символы и т. Д. Но интересующий меня раздел: Конец файла, имеет только ASCII.

Пример, файл:

001
002
003  <<-- first zero on this line is byte 8
004

Наличие байтового смещения 8, которое даст мне строку 3.

Думаю, я мог бы использовать что-то вроде этого, чтобы найти номер строки:

 а. tail -c+(offset + 1) file | wc -l, здесь, +1как tailсчитается из 1.
 б. wc -l file
 с. Тогда tail -n+num где numнаходитсяa - b + 1

Но ... есть ли довольно распространенный инструмент, который может дать мне numнапрямую?


Правка, ошибка: или более очевидное:

head -c+offset file | wc -l
user367890
источник
2
Бинарные файлы не имеют строк.
Кусалананда
@Kusalananda: строки в этом контексте - это данные, разделенные 0x0aбайтами.
user367890
3
Вероятно, не то, что вы спрашиваете, но у Vim есть функция для этого. Он считает смещение от 1, так: :echo byte2line(offset+1).
Satō
@SatoKatsura: Да, и спасибо. Пробовал с vim первым. Но даже с vim -bи vim+ set binary+ открыть файл он был поврежден. (Ах. Внезапно я вспоминаю, какой плагин все испортил). Но, во всяком случае, поскольку я использую это в пакетах и ​​в сочетании с целым рядом сценариев, Vim был заброшен рано. Но +1 в любом случае.
user367890
@ user367890 Бинарный файл может быть 0xaгде угодно. Понятие строк в двоичном файле не имеет смысла.
user207421

Ответы:

14

В вашем примере

001
002
003
004

байт номер 8 - это вторая новая 0строка , а не следующая строка.

Следующее даст вам количество полных строк после $bбайтов:

$ dd if=data.in bs=1 count="$b" | wc -l

Он сообщит 2с bустановленным на 8, и это сообщит 1с bустановленным на 7.

ddУтилита, как он используется здесь, будет читать из файла data.in, и будет читать $bблоки размером 1 байт.

Как справедливо указывает «Икар» в комментариях ниже, использование bs=1неэффективно. В данном конкретном случае эффективнее поменять местами bsи count:

$ dd if=data.in bs="$b" count=1 | wc -l

Это будет иметь тот же эффект, что и первая ddкоманда, но будет считывать только один блок $bбайтов.

В wcутилите подсчитывает новые строки, и «линия» в Unix всегда завершаются символом новой строки. Таким образом, вышеприведенная команда все равно скажет, 2если вы установите bзначение меньше 12 (следующий символ новой строки). Поэтому результат, который вы ищете, - это любое число вышеуказанных конвейерных отчетов плюс 1.

Очевидно, это также подсчитает случайные символы новой строки в части двоичного двоичного файла вашего файла, которая предшествует тексту ASCII. Если вы знали, где начинается бит ASCII, вы можете добавить skip="$offset"в ddкоманду $offsetчисло байтов, которое нужно пропустить в файл.

Кусалананда
источник
@don_crisstihead: unknown option -- c
Кусалананда
@Kusalananda Вы используете головку BSD, варианты там разные
Сергей Колодяжный
@Serg :-) Я хорошо это знаю. Мы не знаем, что использует OP, поэтому я придерживаюсь POSIX.
Кусалананда
1
Как я уже упоминал в Q: количество байтов начинается с 0, а не с 1, поэтому 8 == 0 ...
user367890
@ user367890 В этом случае используйте $(( b - 1 )).
Кусалананда
4

В настоящее время нет такого специализированного инструмента, хотя это довольно легко сделать в python:

#!/usr/bin/env python3
import sys
import os

offset = int(sys.argv[2])
newline = 1
with open(sys.argv[1]) as fd:
    fd.seek(offset)
    while True:
        try:
            byte = fd.read(1)
            if byte == '\n': newline+=1
            #print(byte)
            offset = offset - 1
            fd.seek(offset)
        except ValueError:
            break
print(newline)

Использование простое:

line4byte.py <FILE> <BYTE>

Тестовый забег:

$ cat input.txt
001
002
003
004
$ chmod +x ./line4byte.py                                                     
$ ./line4byte.py input.txt 8                                                  
3

Это очень быстрый и простой скрипт. Он не проверяет, является ли файл пустым или нет, поэтому он работает только с непустыми файлами.

Сергей Колодяжный
источник
4

Отслеживайте увиденные байты и выдайте текущий номер строки, если данное смещение будет в пределах суммы:

perl -E '$off=shift;while(<>){$sum+=length;if($sum>=$off){say $.;exit}}' 8 file

Или в длину:

#!/usr/bin/env perl
use strict;
use warnings;
die "Usage: $0 offset file|-\n" if @ARGV != 2;
my $offset = shift;
shift if $ARGV[0] eq '-';
my $sum;
while (readline) {
    $sum += length;
    if ($sum >= $offset) {
        print "$.\n";
        exit;
    }
}
exit 1;
thrig
источник