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

Python 如何提取在文档中间的第1行到第3行?,python,regex,Python,Regex,如何使用pythonre提取中间部分(第一行、第二行和第三行) --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_c

如何使用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)

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

看起来您正在尝试解析多部分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"
    
  • 它不支持无引号的边界,例如
    内容类型:多部分/相关;边界=文本

  • 它不支持像Debian bugmail(例如,从电子邮件日期时间开始以
    开头)或HTTP/SMTP标识符/状态行这样的邮件存档

  • 使用以下Python正则表达式:

    • ^
      匹配行首
    • $
      匹配行尾
    • [abc]
      匹配任何字符
      a
      b
      c
      (其中一个)
    • [a-z]
      匹配任何字符
      a
      z
    • \-
      转义字符
      -
      ,因此它可以在
      []
      表达式中使用
    • 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=“?(*)”?
    在内容类型标题条目中查找用于边界的文本

    \r?\n--%s(-)?\r?\n
    定位单个边界(其中
    %s
    是 动态添加)。注意:我已通过
    re.escape
    传递边界字符串,以防止其被利用(即,如果它包含一个正则表达式,如
    boundary=“[a-z]”

    实际上,您应该使用Python中支持解析RFC822(电子邮件/mime)文档的
    email
    模块。for this声明:“对于简单的非MIME消息,此根对象的负载可能是包含消息文本的字符串。对于MIME消息,根对象将从其is_multipart()方法返回True,并且可以通过get_payload()和walk()方法访问子部分。”

    更新:我创建了一个
    read_lines
    帮助程序来支持字符串列表(例如
    poplib
    )和字符串列表(例如
    f.read()

    更新:用于在以下位置检测边界的
    --%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
    和两个端点匹配,而
    如果是part
    则用于避免
    无匹配。

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

    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、组2、组3
  • 为了获得我们想要的内容,我们先匹配内容类型(不捕获它),然后再匹配所有内容,直到下一系列新行,然后再使用
    \n+
  • 每一行都用
    ([^\n]+)
    捕获,这意味着“匹配任意数量的非新行字符”
  • 捕获每一行后,我们将使用
    \n+
    吃掉任意数量的新行。第3组之后的最后一行是可选的:
    \n*
    (事实上,如果没有它,正则表达式可以工作。)

  • 您的文档是否总是与此完全相同?并非如此。--089e013d100acf582104f809fd8d是定义不同内容类型块之间边界的边界线。非常感谢您的立即关注!没问题:p您需要定义一些规则。计算机如何找到这些行?它们总是第3行、第4行和第6行吗在边界之后?在这种情况下,我建议您不要使用正则表达式,而是以某种方式循环和计数。同意。如果这些行立即跟随
    Content-t