Как эффективнее разрезать файл на части?

Как эффективнее разрезать файл на части?

Предположим, у меня есть текстовый файл размером 10 МБ foo.txt, и в нем 100 000 строк. Теперь я хочу обрабатывать foo.txtокно за окном, с размером окна 10.

Мой текущий сценарий такой:

for ((i=0;i<$lines;i=i+$step))
do    
    head -$((i+step)) $1 | tail -$step > tmp1
    head -$((i+step)) $2 | tail -$step > tmp2
    setstr=$setstr' '`./accuracy.sh tmp1 tmp2`
done
echo $setstr | awk '{for (i=1;i<=NF;i++) sum+=$i; }END{print sum/NF}'

Но работает медленно. Есть ли какой-то более простой и эффективный способ сделать это?

решение1

Это можно сделать с помощью split:

Вот пример того, как это использовать:

split -l 10 input_file output_file_prefix_

Опция -lозначает--lines=

И это будет разделено input_fileна фрагменты по 10 строк в каждом, в следующие файлы:

output_file_prefix_aa
output_file_prefix_ab
output_file_prefix_ac
...

и так далее.

Для других способов использования splitсм. man splitилиздесь

решение2

Было бы полезно иметь немного больше контекста относительно вашей конечной цели, а не фрагмент кода. В частности, есть ли у вас какой-либо контроль над accuracy.sh?

В любом случае, если вы хотите продолжать использовать bash, то вы можете сделать следующее:

for ((i=0;i<$lines;i+=$step))
do
  let end=i+10
  sed -n $i,${end}p $1 >tmp1
  sed -n $i,${end}p $2 >tmp2
  ...
done

решение3

Не уверен, почему это перенесли со StackOverflow. Пока splitэтосуперпользователь-стиль ответа, вопрос был о программировании. Например, вот ответ, который реализует то, что вы ищете в awk.

Одним из действительно удобных аспектов awkявляется то, как он обрабатывает трубы.

#!/usr/bin/awk -f

BEGIN {
  cmd="/path/to/handler"
}

{
  print | cmd
}

NR % 10 == 0 {
  close(cmd)
}

Ваш cmdбудет открыт повторно, если он закрыт ... и закрывается каждую 10-ю строку, чтобы быть открытым повторно в следующей строке вывода.

Эффект будет заключаться в запуске handlerкаждых 10 строк ввода. В конце файла handlerбудет запущено все оставшиеся строки, а cmdавтоматически закрывается при выходе из awk.

Строго говоря, вам не нужно использовать переменную, например, cmdдля хранения команды... но это упрощает корректировку команды, поскольку в противном случае вам пришлось бы ОЧЕНЬ внимательно следить за опечатками в вашем close().

решение4

Это решение не использует временные файлы. Оно сохраняет каждую строку в буферном массиве, который может содержать десять строк. Каждый раз, когда номер строки делится на десять, оно печатает все строки в буфере.

Очевидная ловушка — когда входной файл (# строк) не делится на десять. Решение — сделать проверки в предложении END{}. Что-то вроде:

$ echo {1..33} | tr \ \\n |\
    awk '{lines=NR} END{ if (lines%10!=0) { print "оставшиеся строки"} }'
оставшиеся линии

# ШАГ1 использовать остаток от деления на десятые, чтобы сделать что-то
$ echo {1..200} |tr \ \\n |\
    awk '{a[NR%10]=$0; если (NR%10==0) {напечатать "десять"} }' | cat -n
     1 десяток
     2 десятка
     3 десятка
     4 десять
     5 десять
     6 десять
     7 десять
     8 десять
     9 десять
    10 десять
    11 десять
    12 десять
    13 десять
    14 десять
    15 десять
    16 десять
    17 десять
    18 десять
    19 десять
    20 десять

# ШАГ 2 сделайте что-нибудь с каждой строкой
$ echo {1..10} | tr \ \\n | awk '{ b+=$0} END {print b}'
55

# собираем все вместе
$ cat каждые10.awk
{
        а[NR%10]=$0;
        если (НР%10==0) {
                для (i в а) {
                        printf "%s+", а[i]
                        б+=а[я];
                }
                распечатать "0=" б;
                б=0
        }
}
$ echo {1..200} | tr \ \\n | awk -f every10.awk | column -s= -t
4+5+6+7+8+9+10+1+2+3+0 55
14+15+16+17+18+19+20+11+12+13+0 155
24+25+26+27+28+29+30+21+22+23+0 255
34+35+36+37+38+39+40+31+32+33+0 355
44+45+46+47+48+49+50+41+42+43+0 455
54+55+56+57+58+59+60+51+52+53+0 555
64+65+66+67+68+69+70+61+62+63+0 655
74+75+76+77+78+79+80+71+72+73+0 755
84+85+86+87+88+89+90+81+82+83+0 855
94+95+96+97+98+99+100+91+92+93+0 955
104+105+106+107+108+109+110+101+102+103+0 1055
114+115+116+117+118+119+120+111+112+113+0 1155
124+125+126+127+128+129+130+121+122+123+0 1255
134+135+136+137+138+139+140+131+132+133+0 1355
144+145+146+147+148+149+150+141+142+143+0 1455
154+155+156+157+158+159+160+151+152+153+0 1555
164+165+166+167+168+169+170+161+162+163+0 1655
174+175+176+177+178+179+180+171+172+173+0 1755
184+185+186+187+188+189+190+181+182+183+0 1855
194+195+196+197+198+199+200+191+192+193+0 1955

Идея здесь заключается в том, чтобы использовать awk для печати блоков по десять строк и обрабатывать их, или обрабатывать напрямую с помощью awk, если операция представляет собой простую арифметику или строковые операции.

Связанный контент