【问题标题】:Regex: Select the First Closest Exact Match Until the End正则表达式:选择第一个最接近的完全匹配直到结束
【发布时间】:2020-02-18 17:59:01
【问题描述】:

目标:从电子邮件线程中提取第一封电子邮件

描述:基于对电子邮件的人工检查,我意识到电子邮件线程中的下一封电子邮件总是以一组 From、Sent、To 和 Subject 开头

测试输入

Hello World from: the other side of the first email

from: this
sent: at
to: that
subject: what

second email

from: this
sent: at
to: that
subject: what


third email

from: this
date: at
to: that
subject: what

fourth email

预期输出

Hello World from: the other side of the first email

尝试失败

当第一封电子邮件中有 from: 时,休息后

(.*)((from:[\s\S]+?)(sent:[\s\S]+?)(to:[\s\S]+?)(subject:[\s\S]+))

From、Sent、To 和 Subject 组重复时,跟随失败

([\s\S]+)((from:(?:(?!from:)[\s\S])+?sent:(?:(?!sent:)[\s\S])+?to:(?:(?!to:)[\s\S])+?subject:(?:(?!subject:)[\s\S])+))

第二次尝试works with PCRE(PHP) 时选择了一个不贪婪的选项(标志)。但是,此选项在 python 中不可用,我无法找到使其工作的方法。

Regex101 demo

【问题讨论】:

  • 很抱歉,我在这里忽略了重点。
  • 您是否只是想从显示的输入中提取Hello World from: the other side of the first email
  • 是的,没错。假设它是整个邮件链中的第一封电子邮件,需要根据建议的查找发件人、发送人、收件人和主题组的方法进行提取。
  • 这对你有用吗? regex101.com/r/dw05sx/6
  • 您可以使用捕获组^(.*)\r?\n\s*\r?\nfrom:.*\r?\nsent:.*\r?\nto:.*\r?\nsubject:.*regex101.com/r/fLbKb4/1

标签: python regex string-matching


【解决方案1】:

要仅获得第一个匹配项,您可以使用捕获组并完全匹配应遵循的内容。

^(.*)\r?\n\s*\r?\nfrom:.*\r?\nsent:.*\r?\nto:.*\r?\nsubject:
  • ^ 字符串开始
  • (.*) 匹配除换行符以外的任何字符 0+ 次
  • \r?\n\s* 使用 \s* 匹配换行符后跟 0+ 次空格字符
  • \r?\nfrom:.* 匹配以from: 开头的下一行
  • \r?\nsent:.* 匹配以sent: 开头的下一行
  • \r?\nto:.* 匹配以to: 开头的下一行
  • \r?\nsubject:.* 匹配以subject: 开头的下一行

请注意,在演示链接中,右上角的全局标志 g 未启用。

Regex demo | Python demo

如果第一行可以跨越多行,并且可以注意跨越以from:sent:to:subject: 开头的任何行,您也可以使用负前瞻。

^(.*(?:\r?\n(?!(?:from|sent|to|subject):).*)*)\r?\n\s*\r?\nfrom:.*\r?\nsent:.*\r?\nto:.*\r?\nsubject:

Regex demo

如果fromsenttosubject之间有空格,可以匹配0+(*)空白字符

^(.*(?:\r?\n(?!(?:from|sent|to|subject):).*)*)\r?\s*\r?\sfrom:.*\r?\s*sent:.*\r?\s*to:.*\r?\s*subject:

Regex demo

【讨论】:

  • 如果电子邮件文本超过一行怎么办?你不应该使用 re.DOTALL 吗?
  • @RonaldAaronson 好点,您可以使用re.DOTALL,但为了限制不必要的回溯,您也可以使用负前瞻。我已将其添加为第二个模式。
  • 尽管对于问题中的示例输入文本来说这是一个可接受的答案,但只要在 from 或任何这些组词之后输入新行,它就会失败。我尝试使用单行标志(re.DOTALL),但它破坏了正则表达式
  • @AfsanAbdulaliGujarati 在这种情况下,一种选择是匹配 regex101.com/r/1LsrcT/1 之后的 0+ 个空格字符
【解决方案2】:

也许我理解错了,但你为什么不这样做呢:

re.compile(r"^.*from:\s(\w+@\w+\.\w+)")

这将在字符串开头的第一个“from:”之后找到“email-form”(组 1)中的第一个字符串。

【讨论】:

  • 这可能会起作用,但这里的要求是仅在有一组 from、send、to 和 subject 时才专门匹配
  • 有没有可能不是这种情况?在我看来,这些数据是高度结构化的。您希望存在哪些其他字符串来强化您的正则表达式?
【解决方案3】:
import re

text = """Hello World from: the other side of the first email

from: this
sent: at
to: that
subject: what

second email

from: this
sent: at
to: that
subject: what


third email

from: this
date: at
to: that
subject: what

fourth email"""

m = re.match(r'.*?(?=^from:[^\n]*\nsent:[^\n]*\nto:[^\n]*\nsubject:[^\n]*$)', text, re.MULTILINE | re.DOTALL)
print(m.group(0))

打印:

Hello World from: the other side of the first email

【讨论】:

    【解决方案4】:

    通常,每封电子邮件都有一个 Message-id: 标头,用于唯一标识该邮件。分组在一个线程中的消息构成一个消息树,基于标头 In-response-to: 标头,将孩子(响应)与父母联系起来。

    您的假设可用于链接缺少 Message-id: 标头或缺少 In-Response-to: 标头的消息,但这种情况很少见,难以关联且容易出错。符合 RFC-822 标准的消息可以由不同的人创作,而不是代表他们发送的人(字段From:Sender:Resent-from:Resent-Sender: 等)建议彻底阅读RFC-2822 ,以便了解如何管理或存档 Internet 邮件。

    Message-id:In-Response-To: 标头在 Internet 消息格式的 RFC-822 定义中,并在这些 RFC 的后续更新中得到保留。这是对消息和答案进行分组的正确方法。它也包含在 RFC-2822 中完成的更新中,因此在对话线程中链接消息时,这些标头的使用听起来应该是强制性的。它也用于 NNTP(新闻)消息,几乎所有邮件阅读器都使用它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多