[英]Multi-line Matching in Python
我已經閱讀了所有可以找到的文章,甚至可以理解其中的一些文章,但作為一個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中可能存在換行符,因此我仍希望捕獲它(並可能用空格替換換行符)。 我目前正在閱讀文件一行,這顯然不會起作用,所以......
我的總體目標是對此進行參數化,以便我可以使用它來提取匹配起始字符串的不同模式(始終是行的開頭),結束字符串(我想要捕獲到的地方)的日志條目以及介於兩者之間的值它們作為標識符。
在此先感謝您的幫助!
克里斯。
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
我是否需要一次處理整個文件? 它們的大小通常為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']
您可以將整個文件讀入字符串,然后可以使用re.split來列出按時間分隔的所有條目。 這是一個例子:
f = open(...)
allLines = ''.join(f.readlines())
entries = re.split(regex, allLines)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.