2013-02-19 3 views
4

У меня есть огромный файл, из которого мне нужны данные для определенных записей. Структура файла:Извлечь несколько строк данных между двумя символами - Regex и Python3

>Entry1.1 
#size=1688 
704 1 1 1 4 
979 2 2 2 0 
1220 1 1 1 4 
1309 1 1 1 4 
1316 1 1 1 4 
1372 1 1 1 4 
1374 1 1 1 4 
1576 1 1 1 4 
>Entry2.1 
#size=6251 
6110 3 1.5 0 2 
6129 2 2 2 2 
6136 1 1 1 4 
6142 3 3 3 2 
6143 4 4 4 1 
6150 1 1 1 4 
6152 1 1 1 4 
>Entry3.2 
#size=1777 
AND SO ON----------- 

Что мне нужно, так это то, что мне нужно извлечь все строки (полную запись) для определенных записей. Для e.x. Мне нужна запись для Entry1.1, чем я могу использовать название записи '> Entry1.1' до next '>' в качестве маркеров в REGEX для извлечения строк между ними. Но я не знаю, как создавать такие сложные выражения REGEX. После того, как у меня есть такое выражение подам ее в цикле:

For entry in entrylist: 
    GET record from big_file 
    DO some processing 
    WRITE in result file 

Что может быть REGEX выполнить такую ​​экстракцию записи для конкретных записей? Есть ли еще какой-нибудь питонический способ достичь этого? Буду признателен за вашу помощь в этом.

AK

+0

Благодарю вас. Я не видел, что вы видели мой ответ. Если вам нужны улучшения, спросите. Например, было бы интересно, чтобы пользователь мог ввести ** 1 1 ** вместо ** 1.1 ** – eyquem

ответ

4

С регулярное выражение

import re 

ss = ''' 
>Entry1.1 
#size=1688 
704 1 1 1 4 
979 2 2 2 0 
1220 1 1 1 4 
1309 1 1 1 4 
1316 1 1 1 4 
1372 1 1 1 4 
1374 1 1 1 4 
1576 1 1 1 4 
>Entry2.1 
#size=6251 
6110 3 1.5 0 2 
6129 2 2 2 2 
6136 1 1 1 4 
6142 3 3 3 2 
6143 4 4 4 1 
6150 1 1 1 4 
6152 1 1 1 4 
>Entry3.2 
#size=1777 
AND SO ON----------- 
''' 

patbase = '(>Entry *%s(?![^\n]+?\d).+?)(?=>|(?:\s*\Z))' 


while True: 
    x = raw_input('What entry do you want ? : ') 
    found = re.findall(patbase % x, ss, re.DOTALL) 
    if found: 
     print 'found ==',found 
     for each_entry in found: 
      print '\n%s\n' % each_entry 
    else: 
     print '\n ** There is no such an entry **\n' 

Объяснение '(>Entry *%s(?![^\n]+?\d).+?)(?=>|(?:\s*\Z))':

1)

%s получает ссылку на запись: 1.1, 2, 2.1 и т.д.

2)

Часть (?![^\n]+?\d) должна сделать проверку.

(?![^\n]+?\d) негативный взгляд вперед утверждение, что говорит, что после %s не должно быть [^\n]+?\d, что есть какие-либо символы [^\n]+? перед цифрой \d

я пишу [^\n] означает «любой символ, кроме символа новой строки \n ».
Я обязан написать это вместо простого .+?, потому что я поставил флаг re.DOTALL, а участок рисунка .+? будет действовать до конца записи.
Однако, я только хочу, чтобы убедиться, что после введенного ссылки (представленного% s в шаблоне), не будет дополнительных цифр до конца строки, введенные ошибки

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

3)

В конце '(>Entry *%s(?![^\n]+?\d).+?), часть .+? поймает полный блок Входа, потому что точка любого символа, содержал строку \n
Это для этой цели я поставил флаг re.DOTALL, чтобы сделать следующий участок шаблона .+?, способный передавать символы новой строки до конца записи.

4)

Я хочу согласующий, чтобы остановить в конце Введения желаемого, а не внутри следующей, так что группа определяется скобками в (>Entry *%s(?![^\n]+?\d).+?) поймает именно то, что мы хотим
Следовательно, В конце я положил положительное утверждение (?=>|(?:\s*\Z)), в котором говорится, что символ, перед которым бегущий неровный .+? должен остановиться, должен совпадать, либо > (начало следующей записи), либо конец строки \Z.
Как нельзя возможно, что конец последней записи не будет точно концом всей строки, я ставлю \s*, что означает «возможные пробелы до самого конца».
Так \s*\Z означает «может быть пробелами, прежде чем врезаться в конец строки» Пробелов являются не blank, \f, \n, \r, \t, \v

+0

Спасибо ... Работал как очарование .... – Bade

+1

Спасибо. Обратите внимание, что я использовал '' found = re.findall (patbase% x, ss, re.DOTALL) '', думая, что возможно, что будет несколько записей с той же ссылкой. Если это не так, вы должны упростить код, написав непосредственно '' print '\ n% s \ n'% re.search (patbase% x, ss, re.DOTALL) .group() '' - '' 'print 'found ==', found'' - это пепел без необходимости – eyquem

0

Не совсем уверен, что вы просите. Это приближает вас? Он поместит все ваши записи в словарные ключи и список всех его записей. Предполагая, что он отформатирован, как я считаю. Имеет ли он повторяющиеся записи? Вот что я получил:

entries = {} 
key = '' 
for entry in open('entries.txt'): 
    if entry.startswith('>Entry'): 
     key = entry[1:].strip() # removes > and newline 
     entries[key] = [] 
    else: 
     entries[key].append(entry) 
+0

Прошу прощения, если я не был достаточно ясен. На самом деле файл слишком большой (~ 1 ГБ), поэтому мне просто нужно совпадение строк и извлечение записи. Ответ от «eyquem» отлично справился с этой задачей. – Bade

+0

Все в порядке. Не нужно извиняться :-) Извините, я не мог больше помочь. – Hoopdady

+1

@Atul Если файл очень большой, может возникнуть проблема с использованием регулярного выражения, поскольку ему необходимо загрузить полный контент файла в память. Изучение файла кусками с регулярным выражением требует некоторых сложных процессов, поскольку желаемая часть текста, поиск с регулярным выражением может перекрывать два куска, если куски не перекрываются. Я чист ? – eyquem

1

Я не хорошо с регулярными выражениями, поэтому я стараюсь искать решения без регулярных выражений всякий раз, когда я могу. В Python, естественное место для хранения итерационных логик в генераторе, и поэтому я хотел бы использовать что-то вроде этого (не-itertools-требуемой версии):

def group_by_marker(seq, marker): 
    group = [] 
    # advance past negatives at start 
    for line in seq: 
     if marker(line): 
      group = [line] 
      break 
    for line in seq: 
     # found a new group start; yield what we've got 
     # and start over 
     if marker(line) and group: 
      yield group 
      group = [] 
     group.append(line) 
    # might have extra bits left.. 
    if group: 
     yield group 

В вашем примере случае, мы получаем:

>>> with open("entry0.dat") as fp: 
...  marker = lambda line: line.startswith(">Entry") 
...  for group in group_by_marker(fp, marker): 
...   print(repr(group[0]), len(group)) 
...   
'>Entry1.1\n' 10 
'>Entry2.1\n' 9 
'>Entry3.2\n' 4 

Одним из преимуществ этого подхода является то, что нам никогда не нужно хранить более одной группы в памяти, поэтому оно удобно для действительно больших файлов. Это не так быстро, как регулярное выражение, хотя, если файл 1 ГБ, вы, вероятно, связаны с I/O.

+0

Отлично, спасибо! ~~ Интересно, как мы можем сделать эту работу над Python 3? ~~ Edit: nevermind, так или иначе я получил выход на Python 2, но не на 3, но он действительно работает на Python 3 в конце концов. – arjan