Java 如何从电子邮件中删除引用的文本并仅显示新文本

Java 如何从电子邮件中删除引用的文本并仅显示新文本,java,email,duplicate-data,Java,Email,Duplicate Data,我正在解析电子邮件。当我看到一封电子邮件的回复时,我想删除引用的文本,这样我就可以将文本附加到上一封电子邮件中(即使是回复) 通常,您会看到: 第一封电子邮件(开始对话) 第二封电子邮件(回复第一封) 这封邮件的输出仅为“这是第二封电子邮件”。虽然不同的电子邮件客户端引用文本的方式不同,但如果有某种方式只获取新的电子邮件文本,这也是可以接受的 当以前的电子邮件存储在磁盘上或可用时,您可以检查由特定接收者发送的所有邮件,以确定哪个是响应文本 您还可以通过检查最后一行的第一个字符来确定引号字符。正常

我正在解析电子邮件。当我看到一封电子邮件的回复时,我想删除引用的文本,这样我就可以将文本附加到上一封电子邮件中(即使是回复)

通常,您会看到:

第一封电子邮件(开始对话)

第二封电子邮件(回复第一封)


这封邮件的输出仅为“这是第二封电子邮件”。虽然不同的电子邮件客户端引用文本的方式不同,但如果有某种方式只获取新的电子邮件文本,这也是可以接受的

当以前的电子邮件存储在磁盘上或可用时,您可以检查由特定接收者发送的所有邮件,以确定哪个是响应文本

您还可以通过检查最后一行的第一个字符来确定引号字符。正常情况下,最后一行始终以相同字符开头

当最后两行以不同字符开头时,您可以尝试第一行,因为有时答案会附加在文本末尾

如果检测到这些字符,则可以删除以该字符开头的最后几行,直到检测到空行或以其他字符开头的行

未测试,更像是伪代码

    String[] lines;

    // Check the size of the array first, length > 2
    char startingChar = lines[lines.length - 1].charAt(0);
    int foundCounter = 0;
    for (int i = lines.length - 2; i >=0; --i) {
        String line = lines[i];

        // Check line size > 0
        if(startingChar == line.charAt(0)){
            ++foundCounter;
        }
    }

    final int YOUR_DECISION = 2; // You can decide
    if(foundCounter > YOUR_DECISION){
        deleteLastLinesHere(startingChar, foundCounter);
    }

通过观察Gmail在这方面的行为,我观察到了他们的策略:

  • 写完整的第二封邮件
  • 附加文本,如: 在[时间戳]上,[第一封电子邮件发件人姓名]写道:
  • 附加完整的第一封电子邮件。 A.如果您的电子邮件是纯文本的,则在第一封电子邮件的每一行前面加上“>”。 B如果是HTML格式,Gmail会在左边空白处显示如下内容:

    左边框:1px实心#CCC; 保证金:0px 0px 0px 0.8ex; 左侧填充:1ex; 用户代理样式表 大宗报价

    然后附加第一封电子邮件的文本


  • 当解析来自Gmail地址的电子邮件时,您可以对其进行反向工程。我没有调查过其他客户机,但他们应该有相同的行为。

    通过几行代码,您几乎可以了解到这一点:

    String newMessage = "";
    for (String line : emailLines) {
      if (!line.matches("^[>].*")) {
        newMessage = newMessage.concat(line);
      }
    }
    
    如有必要,您可以为保留不同引用文本签名的电子邮件客户端添加其他正则表达式检查。

    我使用以下正则表达式来匹配引用文本的引入(最后一个是重要的):


    我知道在某些方面这是过分的(而且可能很慢!),但它工作得很好。请让我知道,如果你发现任何不符合这一点,所以我可以改善它

    正则表达式可以很好地工作,除了它匹配从Subject开始的文本,而忽略“Subject”之前的所有内容

    文本
    --------原始消息--------
    //匹配器从这里开始工作
    
    查看谷歌在这方面的专利:


    总之,他们散列文本的一部分(可能类似于句子),然后查找与前面消息中散列的匹配项。超级快速,他们可能也将此作为线程算法的输入。好主意

    char startingChar=lines[lines.length-1]无法编译。你的意思是
    charstartingchar=lines[lines.length-1].charAt(0)?是的,对不起。正如我所说,这更像是伪代码;)。我会像简单化的方法一样更新答案。{2,6}在引文开头的意义是什么?你能给出一个匹配的例子吗?@Divij它要求:subject、to、from、bcc、cc、date集合中至少有2行,但不超过6行。最低要求看起来是,最高要求是全部6。它们似乎是以任何顺序出现的,因此我保留了订单,但出于质量和性能原因,我想将其绑定。感谢您的回答,我们的订单已满。但是,这一模式并不适用于“2014年9月16日,独立服务测试写道:”请尽快给出解决方案
    
        String[] lines;
    
        // Check the size of the array first, length > 2
        char startingChar = lines[lines.length - 1].charAt(0);
        int foundCounter = 0;
        for (int i = lines.length - 2; i >=0; --i) {
            String line = lines[i];
    
            // Check line size > 0
            if(startingChar == line.charAt(0)){
                ++foundCounter;
            }
        }
    
        final int YOUR_DECISION = 2; // You can decide
        if(foundCounter > YOUR_DECISION){
            deleteLastLinesHere(startingChar, foundCounter);
        }
    
    String newMessage = "";
    for (String line : emailLines) {
      if (!line.matches("^[>].*")) {
        newMessage = newMessage.concat(line);
      }
    }
    
      /** general spacers for time and date */
      private static final String spacers = "[\\s,/\\.\\-]";
    
      /** matches times */
      private static final String timePattern  = "(?:[0-2])?[0-9]:[0-5][0-9](?::[0-5][0-9])?(?:(?:\\s)?[AP]M)?";
    
      /** matches day of the week */
      private static final String dayPattern   = "(?:(?:Mon(?:day)?)|(?:Tue(?:sday)?)|(?:Wed(?:nesday)?)|(?:Thu(?:rsday)?)|(?:Fri(?:day)?)|(?:Sat(?:urday)?)|(?:Sun(?:day)?))";
    
      /** matches day of the month (number and st, nd, rd, th) */
      private static final String dayOfMonthPattern = "[0-3]?[0-9]" + spacers + "*(?:(?:th)|(?:st)|(?:nd)|(?:rd))?";
    
      /** matches months (numeric and text) */
      private static final String monthPattern = "(?:(?:Jan(?:uary)?)|(?:Feb(?:uary)?)|(?:Mar(?:ch)?)|(?:Apr(?:il)?)|(?:May)|(?:Jun(?:e)?)|(?:Jul(?:y)?)" +
                                                  "|(?:Aug(?:ust)?)|(?:Sep(?:tember)?)|(?:Oct(?:ober)?)|(?:Nov(?:ember)?)|(?:Dec(?:ember)?)|(?:[0-1]?[0-9]))";
    
      /** matches years (only 1000's and 2000's, because we are matching emails) */
      private static final String yearPattern  = "(?:[1-2]?[0-9])[0-9][0-9]";
    
      /** matches a full date */
      private static final String datePattern     = "(?:" + dayPattern + spacers + "+)?(?:(?:" + dayOfMonthPattern + spacers + "+" + monthPattern + ")|" +
                                                    "(?:" + monthPattern + spacers + "+" + dayOfMonthPattern + "))" +
                                                     spacers + "+" + yearPattern;
    
      /** matches a date and time combo (in either order) */
      private static final String dateTimePattern = "(?:" + datePattern + "[\\s,]*(?:(?:at)|(?:@))?\\s*" + timePattern + ")|" +
                                                    "(?:" + timePattern + "[\\s,]*(?:on)?\\s*"+ datePattern + ")";
    
      /** matches a leading line such as
       * ----Original Message----
       * or simply
       * ------------------------
       */
      private static final String leadInLine    = "-+\\s*(?:Original(?:\\sMessage)?)?\\s*-+\n";
    
      /** matches a header line indicating the date */
      private static final String dateLine    = "(?:(?:date)|(?:sent)|(?:time)):\\s*"+ dateTimePattern + ".*\n";
    
      /** matches a subject or address line */
      private static final String subjectOrAddressLine    = "((?:from)|(?:subject)|(?:b?cc)|(?:to))|:.*\n";
    
      /** matches gmail style quoted text beginning, i.e.
       * On Mon Jun 7, 2010 at 8:50 PM, Simon wrote:
       */
      private static final String gmailQuotedTextBeginning = "(On\\s+" + dateTimePattern + ".*wrote:\n)";
    
    
      /** matches the start of a quoted section of an email */
      private static final Pattern QUOTED_TEXT_BEGINNING = Pattern.compile("(?i)(?:(?:" + leadInLine + ")?" +
                                                                            "(?:(?:" +subjectOrAddressLine + ")|(?:" + dateLine + ")){2,6})|(?:" +
                                                                            gmailQuotedTextBeginning + ")"
                                                                          );
    
    Text
    -------- Original Message -------- 
    <TABLE border="0" cellpadding="0" cellspacing="0">
      <TBODY>
        <TR>
          <TH align="right" valign="baseline">
          // the matcher starts working from here