正則表達式只有真正可用於簡單的(常規)模式。理論上,正則表達式是表示有限狀態機的一種方式。通常,它們用於詞法分析/詞法分析器(將程序字符串拆分爲一系列令牌)或匹配常規字符串(例如羅馬數字)。
它看起來就像你正在試圖解析多部分MIME文件,例如:
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="mimetest"
This part is ignored.
--mimetest
Content-Type: text/plain
Part 1
--mimetest
Content-Type: text/rtf
\rtf{\par Part 2}
--mimetest--
在這裏,你有兩個內MIME文件外MIME文件。每個mime文檔都有一個標題部分,後面跟着一個空行,後面跟着內容。
最好的方法是編寫一個解析器,將該頭與內容一起讀入字典中。然後,您可以使用正則表達式來定位邊界並提取邊界之間,例如文本:
MIME_STATE_HEADER = 1
MIME_STATE_BODY = 2
def read_lines(text):
if isinstance(text, list):
return text
return re.split(r'\r?\n', text)
def parse_mime(text):
header_line = re.compile(r'^([A-Za-z\-]+): (.*)$')
state = MIME_STATE_HEADER
header = {}
body = []
for line in read_lines(text):
if state == MIME_STATE_HEADER:
if line == '':
state = MIME_STATE_BODY
continue
m = header_line.match(line)
if not m:
raise Exception('Invalid header section: %s' % line)
header[ m.group(1).lower() ] = m.group(2)
elif state == MIME_STATE_BODY:
body.append(line)
return header, body
def mime(text):
header, body = parse_mime(text)
content_type = re.compile(r'multipart/.*; boundary="(.*)"')
m = content_type.match(header['content-type'])
if m:
boundary = re.escape(m.group(1))
matcher = re.compile(r'\r?\n--%s(--)?\r?\n' % boundary)
parts = [ mime(part) for part in matcher.split('\n'.join(body))[1:-2] if part ]
return header, parts
return header, '\n'.join(body)
此代碼將處理各種基於MIME的文件,但有幾個限制/錯誤:
這將不支持標頭包起來,如:
Content-Type: multipart/related;
boundary="text"
它不支持不帶引號的界限,如Content-Type: multipart/related; boundary=text
。
它不支持像Debian bugmail(例如以From email date time
開頭)或HTTP/SMTP標識符/狀態行這樣的郵件存檔。
有了這些Python的正則表達式:
^
線
$
的開始相匹配的線的端部
[abc]
匹配所有的字符的匹配a
,b
,c
(之一)
[a-z]
比賽通過對z
\-
逸出,因此可以在一個[]
表達式中使用的字符-
任何字符a
的
a+
匹配一個或表達的多個實例a
a*
匹配零或表達的多個實例a
.
任何字符
a?
任選匹配a
匹配(即。匹配的a
零個或一個實例)
\r
匹配回車符
(a)
捕獲下一個組中的匹配a
表達的含量 - 通過m.group(n)
- 任何在上面的表達式匹配作爲別的訪問-is
^([A-Za-z\-]+): (.*)$
在MIME頭中的一個頭的條目,使得m.group(1)
是標題名稱(例如,「內容類型」)和m.group(2)
的是,報頭的內容相匹配。
\r?\n
匹配Windows或Linux風格的行結束符(MIME文檔應該使用'\ r \ n',但在本地保存文件時可以轉換爲\n
)。
multipart/.*; boundary="?(.*)"?
在Content-Type標題條目中查找用於邊界的文本。
\r?\n--%s(--)?\r?\n
找到一個單獨的邊界(其中%s
是動態添加的邊界 )。注意:我已通過re.escape
傳遞邊界字符串以防止它被利用(即它包含諸如boundary="[a-z]"
之類的正則表達式)。
實際上,您應該在Python中使用email
模塊,該模塊支持解析RFC822(email/mime)文檔。 documentation表示:「對於簡單的非MIME消息,這個根對象的負載可能是一個包含消息文本的字符串。對於MIME消息,根對象將從它的is_multipart()方法返回True,並且子部分可以通過get_payload()和walk()方法訪問。「
更新:我創建了一個read_lines
幫手,支持字符串列表(例如C poplib
)和一個字符串(例如Cf.read()
)。
UPDATE:的--%s\r?\n(.*)\r?\n--%s
匹配邊界檢測在:
matcher = re.compile(r'--%s\r?\n(.*)\r?\n--%s' % (boundary, boundary))
parts = [ mime(part) for part in matcher.findall(body) ]
有兩個問題:
- 它不換行(這可以通過使用
re.compile(..., re.DOTALL)
來解決匹配結束;
- 太貪婪(它匹配多個部分)
後者不容易用正則表達式解決。解決的辦法是分割的邊界線,從而導致:
[part0, None, part1, None, part2, ..., partN, '--', '']
其中part0
是第一邊界之前的部分。因此[1:-2]
用於刪除part0
和兩個結束匹配,並且if part
用於避免None
匹配。
請問您的文檔你總是看起來像這樣嗎? – keyser
不是真的。 --089e013d100acf582104f809fd8d是定義不同內容類型塊之間邊界的邊界線。非常感謝您的關注! – john
沒問題:p您需要定義一些規則。計算機如何找到這些線?它們總是在邊界之後的第3,4和6行嗎?在這種情況下,我建議你不要使用正則表達式,而只是循環並計數。 – keyser