正则表达式 - 匹配两个字符串之间的所有文本

时间:2012-09-17 01:17:55

标签: python regex regex-negation

我目前正在解析具有以下结构的日志文件:

1)时间戳,以#字符开头,后跟\ n

2)在该时间戳之后发生的任意事件#并且后面跟着\ n

3)重复..

这是一个例子:

#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

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

原因是点与新行不匹配,因此表达式只匹配包含时间戳的行;比赛不会跨越多条线。您可以将"dotall" flag传递给re.compile,以便您的表达式可以跨多行匹配。由于您说“事件编码”可能还包含#字符,因此您可能还希望使用多行标记并在开头将{1}}匹配,以使其仅与{{1}匹配在一行的开头。

答案 2 :(得分: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}(不计算...)。

答案 3 :(得分: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 #
  )*
)

你好像误解了什么是负面的前瞻。当它跟随.*时,正则表达式引擎首先尝试使用尽可能多的字符,然后才检查先行模式。如果前瞻不匹配,它将逐个字符地回溯,直到它完成。

然而,您可以将正面预测与非贪婪.*?一起使用。这里.*?会消耗字符,直到前瞻看到一行开头的#或整个字符串的结尾:

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