Uzyskaj numer linii z przesunięcia bajtu

12

Posiadanie przesunięcia bajtu dla pliku.

Czy istnieje narzędzie, które podaje numer linii dla tego bajtu?

  • Liczba bajtów zaczynająca się od zera, jak w: pierwszy bajt to 0, a nie 1.
  • Numer linii zaczynający się od 1.
  • Plik może zawierać zarówno zwykły tekst, „binarne” obiekty BLOB, znaki wielobajtowe itp. Ale sekcja, która mnie interesuje: Koniec pliku, ma tylko ASCII.

Przykład pliku:

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

Posiadanie przesunięcia bajtu 8, które dałoby mi linię 3.

Chyba mógłbym użyć czegoś takiego do znalezienia numeru linii:

 za. tail -c+(offset + 1) file | wc -l, tutaj +1jak się tailliczy od 1.
 b. wc -l file
 do. Więc tail -n+num gdzie numjesta - b + 1

Ale ... czy jest jakieś dość powszechne narzędzie, które może mi numbezpośrednio dać ?


Edytuj, err: lub bardziej oczywiste:

head -c+offset file | wc -l
użytkownik367890
źródło
2
Pliki binarne nie mają linii.
Kusalananda
@Kusalananda: Linie w tym kontekście to dane oddzielone 0x0abajtami.
user367890
3
Prawdopodobnie nie o to pytasz, ale Vim ma do tego odpowiednią funkcję. Liczy offsetu od 1, więc: :echo byte2line(offset+1).
Satō Katsura
@SatoKatsura: Tak i dziękuję. Najpierw spróbowałem z Vimem. Ale nawet przy + vim -bi + otwartym pliku został uszkodzony. (Ach. Nagle przypominam sobie, która wtyczka to popsuła). Ale w każdym razie, ponieważ używam tego w partiach i w połączeniu z szeregiem skryptów, Vim został wcześnie porzucony. Ale i tak +1. vimset binary
user367890
@ user367890 Plik binarny może mieć 0xadowolne miejsce. Pojęcie wierszy w pliku binarnym jest bez znaczenia.
user207421

Odpowiedzi:

14

W twoim przykładzie

001
002
003
004

bajt numer 8 to druga nowa linia, a nie 0następna linia.

Poniżej podano liczbę pełnych wierszy po $bbajtach:

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

Raportuje 2z bustawionym na 8 i będzie raportował 1z bustawionym na 7.

ddUżyteczność, sposób jest używany tutaj, będą odczytywane z pliku data.ini odczyta $bblokach o rozmiarze 1 bajta.

Jak słusznie wskazuje „Ikar” w poniższych komentarzach, używanie bs=1jest nieefektywne. W tym konkretnym przypadku bardziej wydajne jest zamiana bsi count:

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

Będzie to miało taki sam efekt jak pierwsze ddpolecenie, ale odczyta tylko jeden blok $bbajtów.

Do wcliczy użytkowych znaki nowej linii, i „linia” w Unix zawsze jest zakończony znakiem nowej linii. Tak więc powyższe polecenie nadal powie, 2jeśli ustawisz wartość bniższą niż 12 (następna nowa linia). Wynik, którego szukasz, to zatem dowolna liczba z powyższych raportów potoku plus 1.

To oczywiście policzy również losowe znaki nowej linii w części binarnego obiektu blob, która poprzedza tekst ASCII. Jeśli wiesz, gdzie zaczyna się bit ASCII, możesz dodać skip="$offset"do ddpolecenia, gdzie $offsetjest liczba bajtów, które należy pominąć w pliku.

Kusalananda
źródło
@don_crisstihead: unknown option -- c
Kusalananda
@Kusalananda Używasz głowy BSD, opcje są różne
Sergiy Kolodyazhnyy
@Serg :-) Jestem tego świadomy. Nie wiemy, czego używa OP, więc trzymam się POSIX.
Kusalananda
1
Jak wspomniałem w Q: liczba bajtów zaczyna się od 0, a nie 1, dlatego 8 == 0 ...
367890
@ user367890 W takim przypadku użyj $(( b - 1 )).
Kusalananda
4

Obecnie nie ma takiego dedykowanego narzędzia, chociaż można to zrobić dość łatwo w Pythonie:

#!/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)

Użycie jest proste:

line4byte.py <FILE> <BYTE>

Testowe uruchomienie:

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

To jest bardzo szybki i prosty skrypt. Nie sprawdza, czy plik jest pusty, czy nie, więc działa tylko na niepustych plikach.

Sergiy Kolodyazhnyy
źródło
4

Śledź widoczne bajty i emituj bieżący numer linii, jeśli podane przesunięcie będzie mieściło się w sumie:

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

Lub na długość:

#!/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;
gałązka
źródło