如何将第1行提取到文档中间的第3行?

时间:2014-04-27 18:23:05

标签: python regex

如何使用python re提取中间部分(第一行,第二行和第三行)?

--089e013d100acf582104f809fd8d
Content-Type: text/plain; charset=UTF-8

first line
second line

third line

--089e013d100acf582104f809fd8d

阅读邮箱的第一部分:

#!/usr/bin/env python
import poplib
from email import parser

pop_conn = poplib.POP3_SSL('mail.company.com')
pop_conn.user('user')
pop_conn.pass_('')

#newest email has the highest message number
numMessages = len(pop_conn.list()[1])

(server_msg,body,octets) = pop_conn.retr(numMessages)

2 个答案:

答案 0 :(得分:2)

正则表达式仅适用于简单(常规)模式。从理论上讲,正则表达式是一种表示有限状态机的方法。通常,它们用于标记化/词法分析器(将程序字符串拆分为一系列标记)或匹配常规字符串(例如罗马数字)。

您似乎正在尝试解析多部分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的文档,但有一些限制/错误:

  1. 这不支持包装的标题,例如:

    Content-Type: multipart/related;
        boundary="text"
    
  2. 它不支持不带引号的边界,例如Content-Type: multipart/related; boundary=text

  3. 它不支持Debian bugmail等邮件存档(例如以From email date time开头)或HTTP / SMTP标识符/状态行。

  4. 使用这些Python正则表达式:

    • ^匹配行的开头
    • $匹配行尾
    • [abc]匹配任何字符abc(其中一个)
    • [a-z]匹配任何字符az
    • \-转义字符-,因此可以在[]表达式中使用
    • a+匹配表达式a
    • 的一个或多个实例
    • a*匹配表达式a
    • 的零个或多个实例
    • .匹配任何字符
    • a?可选地匹配a(即匹配a的零个或一个实例)
    • \r匹配回车符号
    • (a)捕获下一组中匹配的a表达式的内容 - 通过m.group(n)
    • 访问
    • 上述表达式中的任何其他内容均按原样匹配

    ^([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帮助器来支持字符串列表(例如来自poplib)和一个字符串(来自例如f.read())。< / p>

    更新:用于边界检测的--%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) ]
    

    有两个问题:

    1. 它与新行不匹配(可以使用re.compile(..., re.DOTALL);
    2. 解决
    3. 它太贪婪了(它匹配多个部分)。
    4. 使用正则表达式无法轻易解决后者问题。解决方案是在边界字符串上拆分,结果是:

      [part0, None, part1, None, part2, ..., partN, '--', '']
      

      其中part0是第一个边界之前的部分。因此[1:-2]用于删除part0和两个结束匹配,if part用于删除None匹配。

答案 1 :(得分:0)

以下是我用来捕捉三行的简单代码:

reobj = re.compile("Content-Type:.*?\n+([^\n]+)\n+([^\n]+)\n+([^\n]+)\n*")
match = reobj.search(subject)
if match:
    line1 = match.group(1)
    line2 = match.group(2)
    line3 = match.group(3)
else:
    result = ""

这是如何运作的?

  1. 我们将捕获一组捕获括号之间的每一行。他们将是第1组,第2组,第3组。
  2. 要获得我们想要的内容,我们会匹配Content-Type(不捕获它),然后匹配下一系列新行,我们匹配而不会使用\n+
  3. 使用([^\n]+)捕获每一行,这意味着&#34;匹配任何不是新行的任意数量的字符。
  4. 在捕获每一行后,我们会使用\n+占用任意数量的新行。第3组之后的最后一个换行符是可选的:\n*(事实上正则表达式可以在没有它的情况下工作。)