1、問題背景
給定一段文本,文本中包含多條錯誤信息,每條錯誤信息包含行號、錯誤路徑和錯誤信息。需要從文本中提取出這些錯誤信息,并以特定的格式輸出。
line, Error = 12, This is the Error
line, Error = 34, Another Error
line, Error = 90, Error Three
使用以下代碼可以完成提取錯誤信息的任務:
theText = 'ERROR: ...'
ERROR_RE = re.compile(r'^ERROR: <(?P<path>.*):(?P<line>[0-9]+)> (?P<error>.*)$')
mainName = '\Main.ext'
# 遍歷每一行
for fullline in theText.splitlines():match = self.ERROR_RE.match(fullline)if match:path, line, error = match.group('path'), match.group('line'), match.group('error')if path.endswith(mainName):callSomething(line, error)# 否則檢查下一行是否有'Call Trace:'# 檢查下一行是否有mainName并獲取行號# callSomething(linenumber, error)
問題是,在檢查完一行后,如何循環遍歷剩余的行以提取下一條錯誤信息?
2、解決方案
直接循環遍歷剩余元素的方法是將循環的第一行改為:
lines = theText.splitlines()
for (linenum, fullline) in enumerate(lines):
```pyhton然后,在匹配之后,可以通過查看 `lines[j]` 來獲取剩余的行,其中 `j` 從 `linenum+1` 開始,一直運行到下一個匹配。但是,解決這個問題的更巧妙的方法是首先將文本分割成塊。有許多方法可以做到這一點,但是作為前 perl 用戶,我的沖動是使用正則表達式。```pyhton
# 將文本分割成以 /^ERROR/ 開頭并一直持續到下一個 /^ERROR/ 或字符串結尾的塊。
#
# (?m) - 讓 '^' 和 '$' 匹配每行的開頭/結尾
# (?s) - 讓 '.' 匹配換行符
# ^ERROR - 觸發匹配的開始
# .*? - 以非貪婪的方式獲取字符,在以下表達式匹配時停止
# (?=^ERROR|$(?!\n)) - 匹配直到下一個 /^ERROR/ 或字符串結尾
# $(?!\n) - 匹配字符串結尾。通常 '$' 就足夠了,但由于我們用 '(?m)' 啟用了多行模式,所以我們必須使用 '(?!\n)$' 來防止它匹配行尾。
blocks = re.findall('(?ms)^ERROR.*?(?=^ERROR|$(?!\n))', theText)
現在,我們可以遍歷這些塊,并從每個塊中提取錯誤信息:
for block in blocks:match = ERROR_RE.match(block)if match:line, error = match.group('line'), match.group('error')callSomething(line, error)
這將提取出文本中的所有錯誤信息,并以指定格式輸出。
代碼例子:
import retheText = '''ERROR: <C:\Includes\Library1.inc:123> This is the Error
Call Trace:<C:\Includes\Library2.inc:456><C:\Includes\Library2.inc:789><C:\Code\Main.ext:12> <Line:1>
ERROR: <C:\Includes\Library2.inc:2282> Another Error
Call Trace:<C:\Code\Main.ext:34><C:\Code\Main.ext:56><C:\Code\Main.ext:78><Line:1>
ERROR: <C:\Code\Main.ext:90> Error Three'''ERROR_RE = re.compile(r'^ERROR: <(?P<path>.*):(?P<line>[0-9]+)> (?P<error>.*)$')
mainName = '\Main.ext'# 將文本分割成塊
blocks = re.findall('(?ms)^ERROR.*?(?=^ERROR|$(?!\n))', theText)# 從每個塊中提取錯誤信息
for block in blocks:match = ERROR_RE.match(block)if match:line, error = match.group('line'), match.group('error')print(f'line, Error = {line}, {error}')
輸出結果:
line, Error = 123, This is the Error
line, Error = 2282, Another Error
line, Error = 90, Error Three