Как максимально быстро прочитать файл через каждый N-й символ?

Я уже знаю пару способов прочитать файл через каждые N символов в bash:

LC_ALL=C
while read -n100 character; do
    echo "$character"
done < <(cat "$@" | tr -d '\n')
    echo "$character

Но пока это работает, я хотите узнать более быстрые способы сделать это либо в Bash, либо с помощью инструментов posix/unix.

Есть ли другой способ сделать это быстрее?

1
18.06.2020, 16:32
2 ответа

Попробуйте это awkрешение, которое работает со многими, но не со всеми awkреализациями из-за использования расширения "пустой разделитель полей":

awk -F "" -v l=100 '{last=0; for (i=1;i<=NF;i++) {if ((i+carry)%l==0) {print $i; last=i}};\
                     if (last) {carry=NF-last} else {carry+=(NF-l)}}' inputfile.txt

Это будет обрабатывать каждый символ как одно поле(-F "")и печатать только те поля, где номер поля по модулю «длина пропуска»l(в вашем случае 100 )равен нулю, принимая во внимание перенос -над, но без учета новых строк.

Обратите внимание, что поскольку счет начинается с 1, первый символ , а не считывается. Вы можете использовать

awk -F "" -v l=10 -v ofs=1 '{last=0; for (i=1;i<=NF;i++) {if ((i+carry)%l==ofs) {print $i; last=i}};\
                             if (last) {carry=NF-last+ofs} else {carry+=(NF-l)}}' inputfile.txt

, чтобы отрегулировать смещение с помощью ofs.

Тестовый пример

Протестировано с gawk, mawkи nawkв системе Linux.

  • Входной файл
    12345678901234
    567890123
    4567890123456789012
    34567890123
    
  • Вывод "каждый 10-й, начиная с 10-го символа"
    $ awk -F "" -v l=10 '{last=0; for (i=1;i<=NF;i++) {if ((i+carry)%l==0) {print $i; last=i}}; if (last) {carry=NF-last} else {carry+=(NF-l)}}' testfile.txt 
    0
    0
    0
    0
    0
    
  • Вывод "каждый 10-й, начиная с первого символа"
    $ awk -F "" -v l=10 -v ofs=1 '{last=0; for (i=1;i<=NF;i++) {if ((i+carry)%l==ofs) {print $i; last=i}}; if (last) {carry=NF-last+ofs} else {carry+=(NF-l)}}' testfile.txt 
    1
    1
    1
    1
    1
    1
    
1
18.03.2021, 23:26

Копирование результата AdminBee с несколько более простым кодом.

Входные данные такие же, как у них:

12345678901234
567890123
4567890123456789012
34567890123

Каждый 10-й символ, начиная с 10:

$ fold -w 1 file | awk 'NR % 10 == 0'
0
0
0
0
0

То же самое, но начиная с 1:

$ fold -w 1 file | awk 'NR % 10 == 1'
1
1
1
1
1
1

То же, но начиная с 2:

$ fold -w 1 file | awk 'NR % 10 == 2'
2
2
2
2
2
2

С точки зрения производительности это сравнимо с решением AdminBee awk, но немного быстрее на больших входных данных ("большие входные данные" == приведенные выше тестовые данные повторяются много раз ).

fold -w 1создает одну строку для каждого символа ввода, отбрасывая символы новой строки. Обе используемые команды являются стандартными утилитами POSIX.

4
18.03.2021, 23:26

Теги

Похожие вопросы