2012-09-17 21 views
2

Я в настоящее время синтаксического анализа лог-файл, который имеет следующую структуру:Regex - соответствие всего текста между двумя строками

1) штамп времени, которому предшествует символ # и последующий \ п

2) произвольной # о событиях, произошедших после этой временной отметки и все следуют \ п

3) повторить ..

Вот exmaple:

#100 
04! 
03! 
02! 
#1299 
0L 
0K 
0J 
0E 
#1335 
06! 
0X# 
0[# 
b1010 Z$ 
b1x [$ 
... 

Пожалуйста, простите, казалось бы, загадочные значения, они представляют собой кодировки, представляющие определенные «события».

Примечание: Коды событий могут также использовать символ #.

Что я пытаюсь сделать, это подсчитать количество событий, которые происходят в определенное время.

Другими словами, в момент 100 произошло 3 события.

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

Я использую регулярных выражений Python, и я использую следующее выражение:

pattern = re.compile('(#[0-9]{2,}.*)(?!#[0-9]+)')

Примечание: В {2}, потому что я хочу, метки времени, по крайней мере, двумя цифрами.

Я сопоставляю временную метку, продолжаю соответствовать любым другим символам, пока не нажму другую временную метку, - закончив сопоставление.

Что это возвращает это:

#100 
#1299 
#1335 

Таким образом, я получаю метки времени - но ни один из данных событий - то, что я действительно волнует!

Я думаю, что причина в том, что негативный взгляд - «жадный», но я не совсем уверен.

Может быть совершенно другое регулярное выражение, которое делает это намного проще - открывайте любые предложения!

Любая помощь очень ценится!

-k

ответ

1

Если вы настаиваете на регулярных выражений на основе решения, я предлагаю следующее:

>>> pat = re.compile(r'(^#[0-9]{2,})\s*\n((?:[^#].*\n)*)', re.MULTILINE) 
>>> for t, e in pat.findall(s): 
...  print t, e.count('\n') 
... 
#100 3 
#1299 4 
#1335 6 

Объяснение:

(    
^   anchor to start of line in multiline mode 
    #[0-9]{2,} line starting with # followed by numbers 
) 
\s*   skip whitespace just in case (eg. Windows line separator) 
\n    new line 
(
    (?:   repeat non-capturing group inside capturing group to capture 
       all repetitions 
    [^#].*\n line not starting with # 
)* 
) 

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

Вы можете, однако, использовать положительный lookahead вместе с неживым .*?. Здесь .*? будет потреблять символы, пока опережения не видит либо # в начале строки или конец целой строки:

re.compile(r'(^#[0-9]{2,})\s*\n(.*?)(?=^#|\Z)', re.DOTALL | re.MULTILINE) 
+0

Не могли бы вы объяснить, почему эта версия работает в отличие от других? Благодаря! – kbarber

+0

@kbarber Добавлено некоторое объяснение. –

1

Причина заключается в том, что точка не совпадает с новой строки, поэтому ваше выражение будет соответствовать только строки, содержащие метку времени; матч не будет проходить через несколько строк. Вы можете передать "dotall" flag в re.compile, чтобы ваше выражение соответствовало нескольким строкам. Поскольку вы говорите, что «кодировки событий» могут также содержать символ #, вы также можете использовать многострочный флаг и привязать свое совпадение с ^ в начале, поэтому он соответствует только # в начале строки.

+0

+1 для объяснения –

+0

это то, что вы имели в виду? 'pattern = re.compile ('^ (# [0-9] {2,}. *) (?! # [0-9] +)', re.DOTALL | re.MULTILINE)' - эта версия соответствует ВСЕ текст. Дайте мне знать, если я интерпретирую что-то не так. – kbarber

+0

@kbarber. Отрицательный lookahead соответствует где угодно, кроме как перед '# [0-9] +'. Он также совпадает в конце строки. Поэтому жадный '. *' Способен сопоставлять весь текст. –

1

Вы можете просто прокручивать данные по строкам и иметь словарь, который просто хранит количество событий, связанных с каждой меткой времени; не требуется регулярное выражение.Например:

with open('exampleData') as example: 
    eventCountsDict = {} 
    currEvent = None 
    for line in example: 
     if line[0] == '#': # replace this line with more specific timestamp details if event encodings can start with a '#' 
      eventCountsDict[line] = 0 
      currEvent = line 
     else: 
      eventCountsDict[currEvent] += 1 

print eventCountsDict 

, что код печатает {'#1299\n': 4, '#1335\n': 5, '#100\n': 3} для вашего примера данных (не считая ...).

+0

приятно, но 'collections.defaultdict (int)' или даже 'collections.Counter' является более изящным выбором здесь – wim

+0

@wim ah' collections' и 'itertools' - я всегда забываю использовать вас до тех пор, пока не получу некоторые код уже работает ... –

+0

Спасибо, это хорошая альтернатива. Я забыл упомянуть, что файл также имеет данные заголовка в начале - это не будет учитываться. Что было бы самым элегантным решением не начинать добавлять в dict до тех пор, пока не будет найдена действительная временная метка? Вы бы использовали флаг? - это то, что приходит на ум сначала, но может быть более чистый путь. – kbarber

2

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

>>> import collections 
>>> d = collections.defaultdict(list) 
>>> with open('/tmp/spam.txt') as f: 
... t = 'initial' 
... for line in f: 
...  if line.startswith('#'): 
...  t = line.strip() 
...  else: 
...  d[t].append(line.strip()) 
... 
>>> for k,v in d.iteritems(): 
... print k, len(v) 
... 
#1299 4 
#100 3 
#1335 6 
+0

+1 мой ответ + 'коллекции' –

+0

Ваш переводчик использует 2 пробела для отступов ...? –

+0

В python вы можете использовать 1 пространство, 3 места, что бы вы ни предпочли .. до тех пор, пока вы согласны. Мне нравится 2 пространства лично. – wim

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