2017-01-03 6 views
1

Я ищу, чтобы вытащить определенные группы строк из больших текстовых файлов (~ 870 000 000 строк). Например, в 50-строчном файле мне могут понадобиться строки 3-6, 18-27 и 39-45.Чтение групп строк из большого текстового файла

от просмотра переполнения стека, я обнаружил, что команда Баш:

tail -n+NUMstart file |head -nNUMend 

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

Есть ли лучший способ сделать это, чем использовать приведенную выше команду для каждой группы линий? Я предполагаю, что ответ скорее всего будет командой bash, но я действительно открыт для любого языка/инструмента, который лучше всего выполнит эту работу.

ответ

3

Чтобы показать линии 3-6, 18-27 и 39-45 с СЭД:

sed -n "3,6p;18,27p;39,45p" file 

Также можно кормить из СЭД файл.

Содержимое файла foobar:

 
3,6p 
18,27p 
39,45p 

Использование:

sed -n -f foobar file 
+0

Мне любопытно, если бы это было намного быстрее, чем 'awk'. – codeforester

+1

сканирует весь файл, не может быть быстрее, чем 'awk' с выходом после последней строки. – karakfa

+2

Добавление '45q' в качестве последней команды исправит это. –

0

Проблема с tail -n XX file | head -n YY для разных диапазонов заключается в том, что вы запускаете ее несколько раз, следовательно, неэффективность. В противном случае benchmarks считают, что это лучшее решение.

Для этого конкретного случая, вы можете использовать awk:

awk '(NR>=start1 && NR<=end1) || (NR>=start2 && NR<=end2) || ...' file 

В вашем случае:

awk '(NR>=3 && NR<=6) || (NR>=18 && NR<=27) || (NR>=39 && NR<=45)' file 

То есть, вы группировать диапазоны и позволяют awk печатать соответствующие линии, когда они происходят, просто перебирая файл один раз. Также может быть полезно добавить окончательный NR==endX {exit} (endX, являющийся закрывающим элементом из последнего диапазона), чтобы он завершил обработку после того, как прочитал последнюю интересную строку.

В вашем случае:

awk '(NR>=3 && NR<=6) || (NR>=18 && NR<=27) || (NR>=39 && NR<=45); NR==45 {exit}' file 
1

awk на помощь!

awk -v lines='3-6,18-27,39-45' ' 
     BEGIN {n=split(lines,a,","); 
       for(i=1;i<=n;i++) 
       {split(a[i],t,"-"); 
       rs[++c]=t[1]; re[c]=t[2]}} 

      {for(i=s;i<=c;i++) 
       if(NR>=rs[i] && NR<=re[i]) {print; next} 
       else if(NR>re[i]) s++; 
       if(s>c) exit}' file 

обеспечивает ранний выход после последней печатной строки. Нет проверки ошибок, диапазоны должны предоставляться в порядке возрастания.

+0

Хороший подход. Тем не менее, я не думаю, что диапазон подачи является особой проблемой здесь, поэтому блок BEGIN каким-то образом не имеет значения (для меня). – fedorqui

Смежные вопросы