Python中的多行匹配

时间:2013-08-28 17:30:27

标签: python regex

我已经阅读了所有可以找到的文章,甚至了解了其中的一些文章,但作为一个Python新手,我仍然有点失落并希望得到帮助:)

我正在编写一个脚本来解析特定于应用程序的日志文件中感兴趣的项目,每行都以一个我可以匹配的时间戳开头,我可以定义两个东西来识别我想要捕获的内容,一些是内容和字符串将终止我想要提取的内容。

我的问题是多行,在大多数情况下,每个日志行都以换行符终止,但有些条目包含可能在其中包含新行的SQL,因此会在日志中创建新行。

所以,在一个简单的例子中,我可能会这样:

[8/21/13 11:30:33:557 PDT] 00000488 SystemOut     O 21 Aug 2013 11:30:33:557 [WARN] [MXServerUI01] [CID-UIASYNC-17464] BMXAA6720W - USER = (ABCDEF) SPID = (2526) app (ITEM) object (ITEM) : select * from item  where ((status != 'OBSOLETE' and itemsetid = 'ITEMSET1') and (exists (select 1 from maximo.invvendor where (exists (select 1 from maximo.companies where (( contains(name,'  $AAAA  ') > 0 )) and (company=invvendor.manufacturer and orgid=invvendor.orgid))) and (itemnum = item.itemnum and itemsetid = item.itemsetid)))) and (itemtype in (select value from synonymdomain where domainid='ITEMTYPE' and maxvalue = 'ITEM')) order by itemnum asc  (execution took 2083 milliseconds)

这一切都显示为一行,我可以与此匹配:

re.compile('\[(0?[1-9]|[12][0-9]|3[01])(\/)(0?[1-9]|[12][0-9]|3[01])(\/)([0-9]{2}).*(milliseconds)')

但是在某些情况下,SQL中可能会出现换行符,因此我仍希望捕获它(并可能用空格替换换行符)。我目前正在读取一行文件,这显然不会起作用......

  1. 我是否需要一次性处理整个文件?它们的大小通常为20mb。如何读取整个文件并遍历查找单行或多行块?
  2. 我如何编写一个多行RegEx,它可以匹配一行中的整个内容,也可以分散在多行中?
  3. 我的总体目标是对此进行参数化,以便我可以使用它来提取匹配起始字符串的不同模式(始终是行的开头),结束字符串(我想要捕获的位置)和值的日志条目它们之间是一个标识符。

    提前感谢您的帮助!

    克里斯。

    import sys, getopt, os, re
    
    sourceFolder = 'C:/MaxLogs'
    logFileName = sourceFolder + "/Test.log"
    lines = []
    print "--- START ----"
    lineStartsWith = re.compile('\[(0?[1-9]|[12][0-9]|3[01])(\/)(0?[1-9]|[12][0-9]|3[01])(\/)([0-9]{2})(\ )')
    lineContains = re.compile('.*BMXAA6720W.*')
    lineEndsWith = re.compile('(?:.*milliseconds.*)')
    
    lines = []
    with open(logFileName, 'r') as f:
        for line in f:
            if lineStartsWith.match(line) and lineContains.match(line):
                if lineEndsWith.match(line) :
                    print 'Full Line Found'
                    print line
                    print "- Record Separator -"
                else:
                    print 'Partial Line Found'
                    print line
                    print "- Record Separator -"
    
    print "--- DONE ----"
    

    下一步,对于我的部分行,我将继续阅读,直到找到lineEndsWith并将行汇编到一个块中。

    我不是专家所以欢迎提出建议!

    更新 - 所以我有它工作,感谢所有帮助指导事情的反应,我意识到它不漂亮我需要清理我的if / elif混乱并使其更有效但IT工作!感谢您的帮助。

    import sys, getopt, os, re
    
    sourceFolder = 'C:/MaxLogs'
    logFileName = sourceFolder + "/Test.log"
    
    print "--- START ----"
    
    lineStartsWith = re.compile('\[(0?[1-9]|[12][0-9]|3[01])(\/)(0?[1-9]|[12][0-9]|3[01])(\/)([0-9]{2})(\ )')
    lineContains = re.compile('.*BMXAA6720W.*')
    lineEndsWith = re.compile('(?:.*milliseconds.*)')
    
    lines = []
    
    multiLine = False
    
    with open(logFileName, 'r') as f:
        for line in f:
            if lineStartsWith.match(line) and lineContains.match(line) and lineEndsWith.match(line):
                lines.append(line.replace("\n", " "))
            elif lineStartsWith.match(line) and lineContains.match(line) and not multiLine:
                #Found the start of a multi-line entry
                multiLineString = line
                multiLine = True
            elif multiLine and not lineEndsWith.match(line):
                multiLineString = multiLineString + line
            elif multiLine and lineEndsWith.match(line):
                multiLineString = multiLineString + line
                multiLineString = multiLineString.replace("\n", " ")
                lines.append(multiLineString)
                multiLine = False
    
    for line in lines:
        print line
    

2 个答案:

答案 0 :(得分:3)

  

我是否需要一次性处理整个文件?它们的大小通常为20mb。如何读取整个文件并迭代查找单行或多行块?

这里有两种选择。

您可以逐块读取文件,确保将每个块末尾的任何“剩余”位附加到下一个块的开头,然后搜索每个块。当然,你必须通过查看你的数据格式是什么以及你的正则表达式可以匹配的东西来弄清楚什么算作“剩余”,理论上,多个块可能都算作剩余......

或者您可以mmap该文件。 mmap就像一个字节(或者像Python 2.x中的str一样),并将其留给操作系统来根据需要处理和调出分页块。除非你试图处理绝对庞大的文件(32位千兆字节,64位甚至更多),否则这是微不足道和高效的:

with open('bigfile', 'rb') as f:
    with mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) as m:
        for match in compiled_re.finditer(m):
            do_stuff(match)

在旧版本的Python中,mmap不是上下文管理器,因此您需要在其周围包裹contextlib.closing(或者如果您愿意,只需使用显式close


我如何编写多行RegEx,它可以匹配一行中的整个内容,也可以分散在多行中?

您可以使用DOTALL标记,这会使.匹配换行符。您可以使用MULTILINE标记并添加适当的$和/或^字符,但这会使简单案例变得更加困难,并且很少需要。这是DOTALL的示例(使用更简单的正则表达式使其更明显):

>>> s1 = """[8/21/13 11:30:33:557 PDT] 00000488 SystemOut     O 21 Aug 2013 11:30:33:557 [WARN] [MXServerUI01] [CID-UIASYNC-17464] BMXAA6720W - USER = (ABCDEF) SPID = (2526) app (ITEM) object (ITEM) : select * from item  where ((status != 'OBSOLETE' and itemsetid = 'ITEMSET1') and (exists (select 1 from maximo.invvendor where (exists (select 1 from maximo.companies where (( contains(name,'  $AAAA  ') > 0 )) and (company=invvendor.manufacturer and orgid=invvendor.orgid))) and (itemnum = item.itemnum and itemsetid = item.itemsetid)))) and (itemtype in (select value from synonymdomain where domainid='ITEMTYPE' and maxvalue = 'ITEM')) order by itemnum asc  (execution took 2083 milliseconds)"""
>>> s2 = """[8/21/13 11:30:33:557 PDT] 00000488 SystemOut     O 21 Aug 2013 11:30:33:557 [WARN] [MXServerUI01] [CID-UIASYNC-17464] BMXAA6720W - USER = (ABCDEF) SPID = (2526) app (ITEM) object (ITEM) : select * from item  where ((status != 'OBSOLETE' and itemsetid = 'ITEMSET1') and 
    (exists (select 1 from maximo.invvendor where (exists (select 1 from maximo.companies where (( contains(name,'  $AAAA  ') > 0 )) and (company=invvendor.manufacturer and orgid=invvendor.orgid))) and (itemnum = item.itemnum and itemsetid = item.itemsetid)))) and (itemtype in (select value from synonymdomain where domainid='ITEMTYPE' and maxvalue = 'ITEM')) order by itemnum asc  (execution took 2083 milliseconds)"""
>>> r = re.compile(r'\[(.*?)\].*?milliseconds\)', re.DOTALL)
>>> r.findall(s1)
['8/21/13 11:30:33:557 PDF']
>>> r.findall(s2)
['8/21/13 11:30:33:557 PDF']

正如您所看到的,第二个.*?与换行符匹配就像空格一样容易。

如果你只是想把换行视为空格,你也不需要; '\s'已经抓住换行符。

例如:

>>> s1 = 'abc def\nghi\n'
>>> s2 = 'abc\ndef\nghi\n'
>>> r = re.compile(r'abc\s+def')
>>> r.findall(s1)
['abc def']
>>> r.findall(s2)
['abc\ndef']

答案 1 :(得分:0)

您可以将整个文件读入字符串,然后您可以使用re.split列出按时间分隔的所有条目。这是一个例子:

f = open(...)
allLines = ''.join(f.readlines())
entries = re.split(regex, allLines)