Bash или Python для извлечения блоков из текстовых файлов - PullRequest
1 голос
/ 12 января 2010

У меня огромный текстовый файл, который структурирован как:

SEPARATOR
STRING1
(arbitrary number of lines)
SEPARATOR
...
SEPARATOR
STRING2
(arbitrary number of lines)
SEPARATOR
SEPARATOR
STRING3
(arbitrary number of lines)
SEPARATOR
....

Что меняется только между различными «блоками» файла, так это STRING и содержимое между разделителем. Мне нужно получить скрипт на Bash или Python, который дает STRING_i на входе, дает на выходе файл, содержащий

SEPARATOR
STRING_i
(number of lines for this string)
SEPARATOR

Каков наилучший подход к использованию Bash или Python? Другой вариант? Это также должно быть быстро.

Спасибо

Ответы [ 4 ]

3 голосов
/ 12 января 2010

В Python 2.6 или лучше:

def doit(inf, ouf, thestring, separator='SEPARATOR\n'):
  thestring += '\n'
  for line in inf:
    # here we're always at the start-of-block separator
    assert line == separator
    blockid = next(inf)
    if blockid == thestring:
      # found block of interest, use enumerate to count its lines
      for c, line in enumerate(inf):
        if line == separator: break
      assert line == separator
      # emit results and terminate function
      ouf.writelines((separator, thestring, '(%d)' % c, separator))
      inf.close()
      ouf.close()
      return
    # non-interesting block, just skip it
    for line in inf:
      if line == separator: break

В более старых версиях Python вы можете сделать почти то же самое, но измените строку blockid = next(inf) на blockid = inf.next().

Предположения здесь заключаются в том, что входной и выходной файлы открываются вызывающей стороной (которая также принимает интересные значения thestring и, необязательно, separator), но задача этой функции - закрыть их (например, для максимальной простоты). использования в качестве фильтра конвейера, с inf sys.stdin и ouf sys.stdout); легко настроить, если нужно, конечно.

Удаление assert s ускорит его микроскопически, но мне нравится их роль «проверки работоспособности» (и они также могут помочь понять логику потока кода).

Ключом к этому подходу является то, что файл является итератором (из строк), и итераторы могут быть расширены в нескольких местах (так что мы можем иметь несколько операторов for или специальные вызовы "продвигать итератор", такие как next(inf) и они правильно сотрудничают).

0 голосов
/ 13 января 2010

вы можете использовать (g) awk, который является относительно быстрым инструментом для обработки файлов.

read -p "Enter input: " input
awk -vinput="$input" -vRS="SEPARATOR" '$0~input{ printf RT; print $0; printf RT }' file
0 голосов
/ 12 января 2010

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

  1. Однократное чтение файла и сохранение индекса а) байтового смещения для начала каждого STRING_I и б) длины (байтов) блока - расстояния до следующего СЕПАРАТОРА в байтах. Вы можете сохранить этот индекс в отдельном файле или в «заголовке» текущего файла
  2. для каждого запроса STRING_I - читать в индексе
    if STRING_I in index:
     file.seek( start_byte_location )
     file.read( length )
     return parse_with_any_of_procedures_above # like @gruszczy's doit() but w/o loop

не переусердствуйте с индексом: используйте указание STRING_I -> (местоположение, длина) и просто запишите его в файл

0 голосов
/ 12 января 2010

Я бы использовал Python и написал бы что-то похожее на это:

import sys

file = open("file", "r")
counter = 0
count = False
for line in file:
  if count:
    counter += 1
  if count and SEPARATOR == line:
    break
  if not count and sys.argv[1] == line:
    count = True
print SEPARATOR, sys.argv[1], counter, SEPARATOR
file.close()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...