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

474
JackWM

Предположим, у меня есть текстовый файл 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 'END' 

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

4

4 ответа на вопрос

5
sampson-chen

Вы можете сделать это с 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или здесь

спасибо, сампсон. Одна проблема с вашим решением состоит в том, что оно будет производить большое количество временных файлов. В моем примере это будет 10000 файлов. В прошлый раз я использовал Java для разделения файла, и мне потребовалось много времени, чтобы удалить их. 11 лет назад 0
@JackWM Как вы удалили временные файлы? Было ли это что-то вроде `rm output_file_prefix_ *`? sampson-chen 11 лет назад 0
`rm` сообщит об ошибках из-за большого количества файлов. И даже если мы обойдем это, удаление все равно займет много времени. 11 лет назад 0
1
Peter Sundstrom

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

Во всяком случае, если вы хотите продолжать использовать Bash, то вы можете сделать

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

Не уверен, почему это было перенесено из 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().

0
Ярослав Рахматуллин

This solution does not use any temporary files. What is does is store every line in a buffer array that can hold ten lines. Every time the line number is divisible by ten, it prints all the lines in the buffer.

The obvious pitfall is when the input file (# lines) is not divisible by ten. The solution is to make checks in an END{} clause. Something like:

$ echo | tr \ \\n |\ awk ' END{ if (lines%10!=0) { print "leftover lines"} }' leftover lines # STEP1 use modulo to do something every tenth $ echo |tr \ \\n |\ awk ' }' | cat -n 1 ten 2 ten 3 ten 4 ten 5 ten 6 ten 7 ten 8 ten 9 ten 10 ten 11 ten 12 ten 13 ten 14 ten 15 ten 16 ten 17 ten 18 ten 19 ten 20 ten # STEP 2 do something with every line $ echo | tr \ \\n | awk '{ b+=$0} END ' 55 # putting it together $ cat every10.awk { a[NR%10]=$0; if (NR%10==0) { for (i in a) { printf "%s+", a[i] b+=a[i]; } print "0=" b; b=0 } } $ echo | 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 

The idea here is to use awk print blocks of ten lines and process that, or process with awk directly if the operation is simple arithmetic or string operations.

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