Dapatkan nomor baris dari byte offset

12

Memiliki byte offset untuk suatu file.

Apakah ada alat yang memberikan nomor baris untuk byte ini?

  • Hitungan byte dimulai dengan nol, seperti pada: byte pertama adalah 0 bukan 1.
  • Nomor baris dimulai dengan 1.
  • File dapat memiliki teks, blob "biner", karakter multibyte, dll. Tetapi bagian yang saya minati: Akhir file, hanya memiliki ASCII.

Contoh, file:

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

Memiliki byte offset 8yang akan memberi saya jalur 3.

Kira saya bisa menggunakan sesuatu seperti ini untuk menemukan nomor baris:

 Sebuah. tail -c+(offset + 1) file | wc -l, Di sini +1sebagai tailjumlah dari 1.
 b. wc -l file
 c. Lalu tail -n+num di mana numadalaha - b + 1

Tapi ... adakah alat yang cukup umum yang bisa memberi saya numlangsung?


Edit, sesat: atau lebih jelas:

head -c+offset file | wc -l
pengguna367890
sumber
2
File biner tidak memiliki garis.
Kusalananda
@ Kusalananda: Baris dalam konteks ini adalah data yang dipisahkan oleh 0x0abyte.
user367890
3
Mungkin bukan yang Anda tanyakan, tetapi Vim memiliki fungsi untuk itu. Ini menghitung offset dari 1, sehingga: :echo byte2line(offset+1).
Satō Katsura
@SatoKatsura: Ya, dan terima kasih. Mencoba dengan vim terlebih dahulu. Tetapi bahkan dengan vim -bdan vim+ set binary+ buka file itu bisa rusak. (Ah. Tiba-tiba saya ingat plugin mana yang mengacaukannya). Tapi, bagaimanapun, ketika saya menggunakan ini dalam batch dan dalam kombinasi dengan berbagai skrip, Vim awalnya ditinggalkan. Tapi +1 tetap.
user367890
@ user367890 File biner dapat ada di 0xamana saja. Konsep garis dalam file biner tidak ada artinya.
user207421

Jawaban:

14

Dalam contoh Anda,

001
002
003
004

byte nomor 8 adalah baris kedua, bukan 0baris berikutnya.

Berikut ini akan memberi Anda jumlah baris penuh setelah $bbyte:

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

Ini akan melaporkan 2dengan bset ke 8 dan akan melaporkan 1dengan bset ke 7.

The ddutilitas, cara itu digunakan di sini, akan membaca dari file tersebut data.in, dan akan membaca $bblok dari ukuran 1 byte.

Seperti yang ditunjukkan "icarus" dengan benar di komentar di bawah, penggunaannya bs=1tidak efisien. Lebih efisien, dalam kasus khusus ini, untuk bertukar bsdan count:

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

Ini akan memiliki efek yang sama dengan ddperintah pertama , tetapi hanya akan membaca satu blok $bbyte.

The wcjumlah utilitas baris, dan "garis" di Unix selalu diakhiri oleh baris baru. Jadi perintah di atas masih akan mengatakan 2jika Anda menetapkan bapa pun yang lebih rendah dari 12 (baris baru berikut). Karenanya, hasil yang Anda cari adalah angka apa pun yang dilaporkan dalam saluran pipa di atas, ditambah 1.

Ini jelas juga akan menghitung baris baru acak di bagian gumpalan biner dari file Anda yang mendahului teks ASCII. Jika Anda tahu di mana bit ASCII dimulai, Anda bisa menambahkan skip="$offset"ke ddperintah, di mana $offsetjumlah byte untuk dilewati ke file.

Kusalananda
sumber
@don_crisstihead: unknown option -- c
Kusalananda
@ Kusalananda Anda menggunakan kepala BSD, opsi ada yang berbeda
Sergiy Kolodyazhnyy
@Serg :-) Saya sangat menyadari itu. Kami tidak tahu apa yang digunakan OP, jadi saya berpegang teguh pada POSIX.
Kusalananda
1
Seperti yang saya sebutkan di Q: jumlah byte dimulai dengan 0, bukan 1, oleh karena itu 8 == 0 ...
user367890
@ user367890 Dalam hal ini, gunakan $(( b - 1 )).
Kusalananda
4

Saat ini tidak ada alat khusus seperti itu, meskipun dapat dilakukan dengan cukup mudah dengan 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)

Penggunaannya sederhana:

line4byte.py <FILE> <BYTE>

Uji coba:

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

Ini adalah skrip yang sangat cepat dan sederhana. Itu tidak memeriksa apakah file tersebut kosong atau tidak, sehingga hanya berfungsi pada file yang tidak kosong.

Sergiy Kolodyazhnyy
sumber
4

Lacak byte yang terlihat dan lepaskan nomor baris saat ini jika offset yang diberikan berada dalam jumlah:

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

Atau panjang lebar:

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