C# 从引用的回复解析电子邮件内容

C# 从引用的回复解析电子邮件内容,c#,ruby,email,email-parsing,C#,Ruby,Email,Email Parsing,我试图找出如何从任何引用的回复文本中解析出电子邮件的文本。我注意到,通常情况下,电子邮件客户会在“某某写的某某日期”或在行前加上尖括号。不幸的是,并不是每个人都这样做。有人知道如何以编程方式检测回复文本吗?我正在使用C#编写这个解析器。电子邮件中没有回复的通用指示符。您所能做的最好的事情就是尝试捕捉最常见的模式,并在遇到新模式时解析它们 请记住,有些人会在引用的文本中插入回复(例如,我的老板会在我问他们的同一行上回答问题),因此无论你做什么,你都可能会丢失一些你想保留的信息。我对此做了大量搜索,

我试图找出如何从任何引用的回复文本中解析出电子邮件的文本。我注意到,通常情况下,电子邮件客户会在“某某写的某某日期”或在行前加上尖括号。不幸的是,并不是每个人都这样做。有人知道如何以编程方式检测回复文本吗?我正在使用C#编写这个解析器。

电子邮件中没有回复的通用指示符。您所能做的最好的事情就是尝试捕捉最常见的模式,并在遇到新模式时解析它们


请记住,有些人会在引用的文本中插入回复(例如,我的老板会在我问他们的同一行上回答问题),因此无论你做什么,你都可能会丢失一些你想保留的信息。

我对此做了大量搜索,以下是我的发现。基本上有两种情况下,你这样做:当你有整个线程时,当你没有。我将其分为两类:

当您有线程时:

如果你有整个系列的电子邮件,你可以达到一个非常高的保证水平,你删除的实际上是引用文本。有两种方法可以做到这一点。首先,您可以使用消息的消息ID、回复ID和线程索引来确定单个消息、它的父消息以及它所属的线程。有关此操作的详细信息,请参阅、或。一旦你重新组装了线程,你就可以删除外部文本(比如To,From,CC等…行),你就完成了

如果您正在处理的邮件没有标题,您还可以使用相似性匹配来确定电子邮件的哪些部分是回复文本。在这种情况下,您必须进行相似性匹配以确定重复的文本。在这种情况下,您可能需要研究一个诸如或之类的问题

不管怎样,如果您对线程过程感兴趣,请查看

当您没有线程时:

如果你只被一条来自该线程的消息所困扰,那么你必须试着猜测该引用是什么。在这种情况下,我看到的不同报价方法如下:

  • 一行(如outlook中所示)
  • 尖括号
  • “--原始消息--”
  • 某日某某写下:

  • 删除下面的文本,就完成了。这些方法的缺点是,它们都假设发送者将其回复放在引用文本的顶部,而不是交错(就像互联网上的旧样式一样)。如果真是这样,祝你好运。我希望这对你们中的一些人有所帮助

    首先,这是一项棘手的任务

    您应该从不同的电子邮件客户端收集典型的响应,并准备正确的正则表达式(或其他什么)来解析它们。我收集了来自outlook、thunderbird、gmail、apple mail和mail.ru的回复

    我使用正则表达式以以下方式解析响应:如果表达式不匹配,我将尝试使用下一个表达式

    new Regex("From:\\s*" + Regex.Escape(_mail), RegexOptions.IgnoreCase);
    new Regex("<" + Regex.Escape(_mail) + ">", RegexOptions.IgnoreCase);
    new Regex(Regex.Escape(_mail) + "\\s+wrote:", RegexOptions.IgnoreCase);
    new Regex("\\n.*On.*(\\r\\n)?wrote:\\r\\n", RegexOptions.IgnoreCase | RegexOptions.Multiline);
    new Regex("-+original\\s+message-+\\s*$", RegexOptions.IgnoreCase);
    new Regex("from:\\s*$", RegexOptions.IgnoreCase);
    
    以下是我收集的一小部分测试响应(样本除以--):

    来自:test@test.com[邮寄至:test@test.com] 
    发送日期:2009年1月13日星期二下午1:27
    ----
    2008/12/26 
    >正文
    ----
    test@test.com写的:
    >正文
    ----
    test@test.com写:文本
    文本
    ----
    2009/1/13 
    >正文
    ----
    test@test.com写:文本
    文本
    ----
    2009/1/13 
    >正文
    >正文
    ----
    2009/1/13 
    >正文
    >正文
    ----
    test@test.com写的:
    >正文
    >正文
    ----
    ---2009年1月23日,星期五,test@test.com写的:
    >正文
    >正文
    

    致以最诚挚的问候,Oleg Yaroshevych

    如果您控制原始消息(例如来自web应用程序的通知),您可以将一个独特的、可识别的头放在适当的位置,并将其用作原始帖子的分隔符。

    谢谢您,Goleg!真的很有帮助。这不是C#,但对于谷歌人来说,这是我的Ruby解析脚本:

    def extract_reply(text, address)
        regex_arr = [
          Regexp.new("From:\s*" + Regexp.escape(address), Regexp::IGNORECASE),
          Regexp.new("<" + Regexp.escape(address) + ">", Regexp::IGNORECASE),
          Regexp.new(Regexp.escape(address) + "\s+wrote:", Regexp::IGNORECASE),
          Regexp.new("^.*On.*(\n)?wrote:$", Regexp::IGNORECASE),
          Regexp.new("-+original\s+message-+\s*$", Regexp::IGNORECASE),
          Regexp.new("from:\s*$", Regexp::IGNORECASE)
        ]
    
        text_length = text.length
        #calculates the matching regex closest to top of page
        index = regex_arr.inject(text_length) do |min, regex|
            [(text.index(regex) || text_length), min].min
        end
    
        text[0, index].strip
    end
    
    def extract_回复(文本、地址)
    regex_arr=[
    Regexp.new(“From:\s*”+Regexp.escape(address),Regexp::IGNORECASE),
    Regexp.new(“,Regexp::IGNORECASE),
    Regexp.new(Regexp.escape(地址)+“\s+写:”,Regexp::IGNORECASE),
    Regexp.new(“^.*On.*(\n)?写:$”,Regexp::IGNORECASE),
    Regexp.new(“-+原始\s+消息-+\s*$”,Regexp::IGNORECASE),
    new(“from:\s*$”,Regexp::IGNORECASE)
    ]
    text_length=text.length
    #计算最接近页面顶部的匹配正则表达式
    索引=正则表达式arr.inject(文本长度)do | min,正则表达式|
    [(text.index(regex)| | text_长度),min].min
    结束
    文本[0,索引].strip
    结束
    

    到目前为止,它运行得非常好。

    到目前为止,最简单的方法是在内容中放置一个标记,例如:

    ---请在这一行以上回复---

    毫无疑问,您已经注意到,解析引用的文本并不是一项简单的任务,因为不同的电子邮件客户端以不同的方式引用文本。为了正确地解决这个问题,您需要在每个电子邮件客户端中进行帐户和测试

    Facebook可以做到这一点,但除非你的项目预算很大,否则你可能无法做到

    Oleg已经解决了使用正则表达式查找“文本”的问题。2012年7月13日13:09,xxx写道:“文本。但是,如果用户像许多人一样删除此文本或在电子邮件底部回复,此解决方案将无法工作

    同样,如果电子邮件客户端使用不同的日期字符串,或者不包含日期字符串,正则表达式将失败。

    这是我的C版本的@hurshagrawal的Ruby代码。我不太了解Ruby,所以可能会关机,但我想我是对的

    public string ExtractReply(string text, string address)
    {
        var regexes = new List<Regex>() { new Regex("From:\\s*" + Regex.Escape(address), RegexOptions.IgnoreCase),
                            new Regex("<" + Regex.Escape(address) + ">", RegexOptions.IgnoreCase),
                            new Regex(Regex.Escape(address) + "\\s+wrote:", RegexOptions.IgnoreCase),
                            new Regex("\\n.*On.*(\\r\\n)?wrote:\\r\\n", RegexOptions.IgnoreCase | RegexOptions.Multiline),
                            new Regex("-+original\\s+message-+\\s*$", RegexOptions.IgnoreCase),
                            new Regex("from:\\s*$", RegexOptions.IgnoreCase),
                            new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                        };
    
        var index = text.Length;
    
        foreach(var regex in regexes){
            var match = regex.Match(text);
    
            if(match.Success && match.Index < index)
                index = match.Index;
        }
    
        return text.Substring(0, index).Trim();
    }
    
    public string ExtractReply(字符串文本、字符串地址)
    {
    var regexes=new List(){new Regex(“From:\\s*”+Regex.Escape(address),RegexOptions.IgnoreCase),
    新正则表达式(“,RegexOptions.IgnoreCase),
    新正则表达式(正则表达式转义(地址)+“\\s+wrot
    
    def extract_reply(text, address)
        regex_arr = [
          Regexp.new("From:\s*" + Regexp.escape(address), Regexp::IGNORECASE),
          Regexp.new("<" + Regexp.escape(address) + ">", Regexp::IGNORECASE),
          Regexp.new(Regexp.escape(address) + "\s+wrote:", Regexp::IGNORECASE),
          Regexp.new("^.*On.*(\n)?wrote:$", Regexp::IGNORECASE),
          Regexp.new("-+original\s+message-+\s*$", Regexp::IGNORECASE),
          Regexp.new("from:\s*$", Regexp::IGNORECASE)
        ]
    
        text_length = text.length
        #calculates the matching regex closest to top of page
        index = regex_arr.inject(text_length) do |min, regex|
            [(text.index(regex) || text_length), min].min
        end
    
        text[0, index].strip
    end
    
    public string ExtractReply(string text, string address)
    {
        var regexes = new List<Regex>() { new Regex("From:\\s*" + Regex.Escape(address), RegexOptions.IgnoreCase),
                            new Regex("<" + Regex.Escape(address) + ">", RegexOptions.IgnoreCase),
                            new Regex(Regex.Escape(address) + "\\s+wrote:", RegexOptions.IgnoreCase),
                            new Regex("\\n.*On.*(\\r\\n)?wrote:\\r\\n", RegexOptions.IgnoreCase | RegexOptions.Multiline),
                            new Regex("-+original\\s+message-+\\s*$", RegexOptions.IgnoreCase),
                            new Regex("from:\\s*$", RegexOptions.IgnoreCase),
                            new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline)
                        };
    
        var index = text.Length;
    
        foreach(var regex in regexes){
            var match = regex.Match(text);
    
            if(match.Success && match.Index < index)
                index = match.Index;
        }
    
        return text.Substring(0, index).Trim();
    }
    
    //Works for Gmail
    new Regex("\\n.*On.*<(\\r\\n)?" + Regex.Escape(address) + "(\\r\\n)?>", RegexOptions.IgnoreCase),
    //Works for Outlook 2010
    new Regex("From:.*" + Regex.Escape(address), RegexOptions.IgnoreCase),