Python: чтение больших файлов по частям

#python

#python

Вопрос:

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

Формат моего файла такой:

0 ххх хххх хххх ххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххх xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1 ххх хххх хххх ххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххх xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Итак, я в основном хочу прочитать фрагмент от 0-1, выполнить свою обработку над ним, затем перейти к фрагменту между 1 и 2.

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

Комментарии:

1. Что не так с техникой регулярных выражений? Это довольно распространенное явление. Пожалуйста, опубликуйте код.

2. Вот пример из neopythonic neopythonic.blogspot.in/2008/10 /…

Ответ №1:

Если все они находятся в пределах одной строки, то есть между «1» и «2» нет разрывов строк. Тогда вы можете выполнить итерацию по строкам файла следующим образом:

 for line in open("myfile.txt"):
    #do stuff
  

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

 for line in open("myfile.txt"):
    if #regex to match start of new string
       parsed_line = line
    else:
       parsed_line  = line
  

и остальной части вашего кода.

Комментарии:

1. Я поставил 1. Однако это содержит несколько сложных случаев, поскольку это связано с тем, что конечное условие становится известным только тогда, когда была прочитана следующая строка данных (строка начинается с «N»). В этом случае последняя прочитанная строка должна использоваться в качестве первых данных следующего элемента. (На самом деле, я думаю, что простое указание на сайтах вызовов того, где parsed_line фактически используется, прояснило бы этот порядок записи; в зависимости от этого есть несколько крайних случаев).

2. @pst хороший улов. Теперь, когда вы указали на них, есть крайние случаи.

Ответ №2:

Почему бы вам просто не прочитать файл по порядку, используя file.read(1) символ за символом?

Затем вы могли бы — на каждой итерации — проверять, пришли ли вы к символу 1 . Затем вы должны убедиться, что сохранение строки происходит быстро.

Ответ №3:

Если буква «N» может только начинать строку, то почему бы не использовать «простое» решение? (Похоже, что это уже делается, я пытаюсь усилить / поддержать это;-))

То есть, просто считываете строку за раз и создаете данные, представляющие текущий N объект. Скажем, после загрузки N = 0 и N = 1 обработайте их вместе, затем переходите к следующей паре (N = 2, N = 3). Единственное, что даже отдаленно сложно, это убедиться, что не выбрасывается прочитанная строка. (Прочитанная строка, которая определяет конечное условие — например, «N» — также содержит данные для следующего N).

Если поиск не требуется (или кэширование ввода-вывода отключено, или на элемент приходится абсурдное количество данных), на самом деле нет причин не использовать readline AFAIK.

Удачного кодирования.


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

 # given an input and previous item data, return either
# [item_number, data, next_overflow] if another item is read
# or None if there are no more items
def read_item (inp, overflow):
  data = overflow or ""

  # this can be replaced with any method to "read the header"
  # the regex is just "the easiest". the contract is just:
  # given "N ....", return N. given anything else, return None
  def get_num(d):
    m = re.match(r"(d ) ", d)
    return int(m.groups(1)) if m else None

  for line in inp:
    if data and get_num(line) ne None:
      # already in an item (have data); current line "overflows".
      # item number is still at start of current data
      return [get_num(data), data, line]

    # not in item, or new item not found yet
    data  = line

  # and end of input, with data. only returns above
  # if a "new" item was encountered; this covers case of
  # no more items (or no items at all)
  if data:
    return [get_num(data), data, None]
  else
    return None
  

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

 # check for error conditions (e.g. None returned)
# note feed-through of "overflow"
num1, data1, overflow = read_item(f, None)
num2, data2, overflow = read_item(f, overflow)
  

Ответ №4:

Если формат фиксирован, почему бы просто не читать по 3 строки за раз с помощью readline()

Комментарии:

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

2. Итак, вы хотите прочитать до строки, начинающейся с числа? нет ли вероятности, что текст в xxxxxx-х может содержать число, которое можно перенести на новую строку? разделены ли строки?

3. Да, ххх могут быть числами, но первые числа для каждой записи являются последовательными, поэтому 1 .. n. Запись разделяется символом n перед следующим порядковым номером.

4. Может ли весь файл поместиться в памяти?

Ответ №5:

Если файл маленький, вы можете прочитать весь файл и разделить () на числовые цифры (возможно, захотите использовать strip(), чтобы избавиться от пробелов и новых строк), затем свернуть список, чтобы обработать каждую строку в списке. Вероятно, вам придется проверить, что результирующая строка, которую вы обрабатываете, изначально не пуста, в случае, если две цифры были рядом друг с другом.

Комментарии:

1. Если вы ищете числа только в начале строк, то вам, вероятно, не следует разделять (на самом деле я не уверен, что разделять вообще полезно для вас), а вместо этого выполнить разделение для комбинации цифр новой строки с помощью регулярных выражений split: [ссылка] docs.python.org/library/re.html#re.split

2. Файл является массивным (около 1,2 ГБ).

3. Если вы ищете набор чисел в начале строк в последовательном порядке… Тогда вам, вероятно, следует написать свою собственную функцию разделения, которая просто выполняет итерацию по строке и разбивает ее в нужное время… Поскольку это массивный файл, я думаю, что phimuemue прав: вы должны читать его посимвольно, и часть вашей обработки должна быть «это последовательность символов новой строки, которая является следующим разделителем?»

Ответ №6:

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

 import re

regx = re.compile('^((d ).*?)(?=^d|Z)',re.DOTALL|re.MULTILINE)

with open(filename) as f:
    text = f.read()

def treat(inp,regx=regx):
    m1  = regx.search(inp)
    numb,chunk = m1.group(2,1)
    li = [chunk]
    for mat in regx.finditer(inp,m1.end()):
        n,ch = mat.group(2,1)
        if int(n) == int(numb)   1:
            yield ''.join(li)
            numb = n
            li = []
        li.append(ch)
        chunk = ch
    yield ''.join(li)

for y in treat(text):
    print repr(y)
  

Этот код выполняется в файле, содержащем :

 1 mountain
orange 2
apple
produce
2 gas
solemn
enlightment
protectorate
3 grimace
song
4 snow
wheat
51 guludururu
kelemekinonoto
52asabi dabada
5 yellow
6 pink 
music
air
7 guitar
blank 8
8 Canada
9 Rimini
  

создает:

 '1 mountainnorange 2napplenproducen'
'2 gasnsolemnnenlightmentnprotectoraten'
'3 grimacensongn'
'4 snownwheatn51 guludururunkelemekinonoton52asabi dabadan'
'5 yellown'
'6 pink nmusicnairn'
'7 guitarnblank 8n'
'8 Canadan'
'9 Rimini'