Отримайте номер рядка зі зміщення байтів


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
 c. Тоді tail -n+num де numєa - b + 1

Але ... чи існує досить поширений інструмент, який може дати мені numбезпосередньо?


Редагувати, помилка: або більш очевидне:

head -c+offset file | wc -l

2
Бінарні файли не мають рядків.
Kusalananda

@Kusalananda: Рядки в цьому контексті - це дані, розділені 0x0aбайтами.
користувач367890

3
Напевно, не те, що ви просите, але Vim має для цього функцію. Він вважає зміщення від 1, так: :echo byte2line(offset+1).
Satō Katsura

@SatoKatsura: Так, і дякую. Спробував спочатку з vim. Але навіть при відкритому файлі vim -bта vim+ set binary+ він зіпсувався. (Ага. Раптом я пригадую, який плагін його псує). Але в будь-якому випадку, оскільки я використовую це в партіях і в поєднанні з низкою сценаріїв, від Vim рано відмовилися. Але +1 все одно.
user367890

@ user367890 Двійковий файл може мати 0xaде завгодно. Поняття рядків у двійковому файлі безглуздо.
користувач207421

Відповіді:


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

@Kusalananda Ви використовуєте голову BSD, варіанти там різні
Сергій Колодяжний,

@Serg :-) Я це добре знаю. Ми не знаємо, що використовує Тхо ОП, тому я дотримуюся POSIX.
Kusalananda

1
Як я вже згадував у Q: кількість байтів починається з 0, а не з 1, тому 8 == 0 ...
user367890

@ user367890 У цьому випадку використовуйте $(( b - 1 )).
Kusalananda

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;

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.