使用C#的正则表达式解析电子邮件头信息

3

我需要将Webhook发布到我的网络应用程序上的表单,并且需要解析电子邮件头地址。

以下是源文本:

Thread-Topic: test subject
Thread-Index: AcwE4mK6Jj19Hgi0SV6yYKvj2/HJbw==
From: "Lastname, Firstname" <firstname_lastname@domain.com>
To: <testto@domain.com>, testto1@domain.com, testto2@domain.com
Cc: <testcc@domain.com>, test3@domain.com
X-OriginalArrivalTime: 27 Apr 2011 13:52:46.0235 (UTC) FILETIME=[635226B0:01CC04E2]

我希望提取以下内容:
<testto@domain.com>, testto1@domain.com, testto2@domain.com

我一整天都在苦苦挣扎,但没有成功使用正则表达式。

5
我个人建议使用一个专门用于解析MIME的库[http://www.aspnetmime.com/]。 - Brad Christie
Brad,我没有完整的消息,只有标题字符串。我不确定 MIME 组件是否能够仅凭此部分工作。 - Kevin Jensen
@Brad Christine,鉴于您评论的赞数,您应该将其发布为答案;) - csharptest.net
5个回答

6
与这里的一些帖子相反,我必须同意mmutz的观点,您无法使用正则表达式解析电子邮件...请参阅本文:https://www.rfc-editor.org/rfc/rfc2822#section-3.4.1 引用如下: 3.4.1. Addr-spec规范 Addr-spec是一个特定的互联网标识符,包含一个由本地解释的字符串,后跟“@”字符(ASCII值为64),后跟互联网域。
“本地解释”的概念意味着只有接收服务器才能解析它。
如果我要尝试解决这个问题,我将查找“To”行内容,分解它,并尝试使用System.Net.Mail.MailAddress解析每个段。
    static void Main()
    {
        string input = @"Thread-Topic: test subject
Thread-Index: AcwE4mK6Jj19Hgi0SV6yYKvj2/HJbw==
From: ""Lastname, Firstname"" <firstname_lastname@domain.com>
To: <testto@domain.com>, ""Yes, this is valid""@[emails are hard to parse!], testto1@domain.com, testto2@domain.com
Cc: <testcc@domain.com>, test3@domain.com
X-OriginalArrivalTime: 27 Apr 2011 13:52:46.0235 (UTC) FILETIME=[635226B0:01CC04E2]";

        Regex toline = new Regex(@"(?im-:^To\s*:\s*(?<to>.*)$)");
        string to = toline.Match(input).Groups["to"].Value;

        int from = 0;
        int pos = 0;
        int found;
        string test;
        
        while(from < to.Length)
        {
            found = (found = to.IndexOf(',', from)) > 0 ? found : to.Length;
            from = found + 1;
            test = to.Substring(pos, found - pos);

            try
            {
                System.Net.Mail.MailAddress addy = new System.Net.Mail.MailAddress(test.Trim());
                Console.WriteLine(addy.Address);
                pos = found + 1;
            }
            catch (FormatException)
            {
            }
        }
    }

上述程序的输出结果:

testto@domain.com
"Yes, this is valid"@[emails are hard to parse!]
testto1@domain.com
testto2@domain.com

这看起来非常有前途...现在正在进行一些单元测试。 - Kevin Jensen
@Blindy 是的,非常“差不多”,我同意。没有库,希望它足够好。 - csharptest.net
是的,我认为“足够好”是正确的术语。我将记录每个请求,并标记任何无法解析的消息,以便在一定量后重新评估。 - Kevin Jensen
@csharptest.net自2017年以来一直使用这段代码而没有出现问题,但突然间我的IDE开始抱怨正则表达式:'Option character' expected。问题在于?im-:部分。跟在“-”符号后面的所有模式都被关闭了,但是在你的表达式中并没有这样的模式。在我看来,这里唯一有意义的事情就是?im(忽略大小写,多行模式),因为C# Regex默认模式是区分大小写和单行的。你也可以使用new Regex(@"(^To\s*:\s*(?<to>.*)$)", RegexOptions.IgnoreCase | RegexOptions.Multiline) - Michael Schnerring

2
RFC 2822兼容的电子邮件正则表达式如下:
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])

只需对文本运行它,您就会获得电子邮件地址。

当然,在不是最佳选项的情况下,总是有不使用正则表达式的选择。但这取决于您!


2
顺便提一下,你的电子邮件的“RFC”正则表达式没有正确处理引用字符串,它无法匹配:"Yes, this is valid"@domain.com - csharptest.net
1
几乎符合RFC标准,我想是吧。这只是证明了,正则表达式不是处理这个问题的最佳工具 :) - Blindy

0
如Blindy所建议的那样,有时你可以老式地解析它。
如果您更喜欢这样做,下面是一种快速方法,假设电子邮件头文本称为“header”:
int start = header.IndexOf("To: ");
int end = header.IndexOf("Cc: ");
string x = header.Substring(start, end-start);

我在减法上可能会有一个字节的偏差,但您可以非常容易地测试和修改它。当然,您还必须确保您的标题中始终有一个Cc:行,否则这将无法正常工作。


0
这里有一个验证电子邮件地址的正则表达式示例,它引用了RFC 2822的更实用实现:
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?

看起来你只想从“收件人”字段中获取电子邮件地址,而且你还需要考虑到<>的问题,因此以下类似的代码可能会起作用:

^To: ((?:\<?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\>?,?(?:\s*))*)

再次提醒,正如其他人所述,您可能不想这样做。但是如果您需要将该输入转换为<testto@domain.com>, testto1@domain.com, testto2@domain.com的正则表达式,则可以使用它。


0

你不能使用正则表达式来解析RFC2822邮件,因为它们的语法包含一个递归产生式(我想是用于注释(嵌套)注释),这使得语法不是正则的。正则表达式(顾名思义)只能解析正则语法。

有关更多信息,请参见RegEx match open tags except XHTML self-contained tags


1
虽然在学术上你是正确的,但任何PCRE(其中C#实现是其中之一)不仅是一个简单的正则表达式解析器,它更接近于上下文无关文法解析器,可以解析递归括号。这是技术超越构造名称的情况。 - Blindy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接