Как удалить цитируемый текст из электронного письма и показывать только новый текст

Я разбираю электронные письма. Когда я вижу ответ на электронное письмо, я хотел бы удалить цитируемый текст, чтобы я мог добавить текст к предыдущему электронному письму (даже если это ответ).

Обычно вы видите это:

1-е электронное письмо (начало разговора)

This is the first email

2-е электронное письмо (ответ на первое)

This is the second email

Tim said:
This is the first email

Результатом будет только «Это второе электронное письмо». Хотя разные почтовые клиенты цитируют текст по-разному, если бы существовал способ получить в основном только новый текст электронного письма, это также было бы приемлемо.


person Tim    schedule 05.03.2010    source источник


Ответы (6)


Я использую следующее регулярное выражение (а), чтобы сопоставить ввод для цитируемого текста (последний - тот, который считается):

  /** 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 + ")"
                                                                      );

Я знаю, что в некотором смысле это перебор (и может быть медленным!), Но работает очень хорошо. Пожалуйста, дайте мне знать, если вы найдете что-то, что не соответствует этому, чтобы я мог это улучшить!

person smurthas    schedule 08.07.2010
comment
Какое значение имеет {2,6} в QUOTED_TEXT_BEGINNING? Вы можете привести пример, которому он подошел бы? - person Divij; 20.02.2014
comment
@Divij требует, чтобы было как минимум 2 и не более 6 строк из набора: subject, to, from, bcc, cc, date. Минимум должен выглядеть и подчиняться, максимум, всем 6. Они, казалось, выполнялись в любом порядке, поэтому я оставил порядок свободным, но хотел связать его по соображениям качества и производительности. - person smurthas; 05.03.2014
comment
Спасибо за ответ, нас очень много. но, этот шаблон не является или для следующей строки 16-09-2014, Indies Services Test написал: пожалуйста, дайте решение как можно скорее - person Uday A. Navapara; 16.09.2014

Ознакомьтесь с патентом Google по этому поводу: http://www.google.com/patents/US7222299

Таким образом, они хэшируют части текста (предположительно, что-то вроде предложений), а затем ищут совпадения с хешами в предыдущих сообщениях. Очень быстро, и они, вероятно, также используют это в качестве входных данных для алгоритма потоковой передачи. Какая отличная идея!

person adam.lofts    schedule 09.08.2013

Когда предыдущие электронные письма хранятся на диске или доступны каким-то образом, вы можете проверить все письма, отправленные конкретным получателем, чтобы определить, какой из них является текстом ответа.

Вы также можете попытаться определить символ кавычки, проверив первый символ последних строк. Обычно последние строки всегда начинаются с одного и того же символа.

Когда последние 2 строки начинаются с другого символа, вы можете попробовать первые строки, потому что иногда ответ добавляется в конце текста.

Если вы обнаружили этот символ, вы можете удалить последние строки, начинающиеся с этого символа, до тех пор, пока не будет обнаружена пустая строка или строка, начинающаяся с другого символа.

НЕ ПРОВЕРЕНО и больше похож на псевдокод.

    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);
    }
person Markus Lausberg    schedule 05.03.2010
comment
char startingChar = lines[lines.length - 1]; не компилируется. Вы имели в виду char startingChar = lines[lines.length - 1].charAt(0);? - person sfussenegger; 05.03.2010
comment
Да, прости. Как я уже сказал, это больше похоже на псевдокод;). Я обновлю ответ - person Markus Lausberg; 05.03.2010

RegEx работает нормально, за исключением того, что сопоставляет текст, начинающийся с «Тема», и игнорирует все, что стоит перед «Тема».

Text
-------- Original Message -------- 
<TABLE border="0" cellpadding="0" cellspacing="0">
  <TBODY>
    <TR>
      <TH align="right" valign="baseline">
      // the matcher starts working from here
person Dmytro    schedule 11.04.2011

Наблюдая за поведением Gmail в этом отношении, я заметил их стратегию:

  1. напишите полное 2-е письмо.
  2. Добавьте текст вроде: В [временная метка] [имя первого отправителя электронной почты] ‹[адрес электронной почты первого отправителя]> написал:
  3. Добавьте полное первое электронное письмо. а. Если ваше электронное письмо представляет собой обычный текст, добавьте '>' перед каждой строкой первого письма. б. Если это в HTML, то Gmail дает левое поле, например:

    граница слева: сплошная 1px #CCC; маржа: 0px 0px 0px 0.8ex; padding-left: 1ex; цитата из таблицы стилей пользовательского агента

    а затем добавляет текст первого письма.

Вы можете перепроектировать это при разборе писем с адреса Gmail. Я не изучал других клиентов, но у них должно быть такое же поведение.

person Monis Iqbal    schedule 05.03.2010

Вы получите это почти правильно, используя пару строк кода:

String newMessage = "";
for (String line : emailLines) {
  if (!line.matches("^[>].*")) {
    newMessage = newMessage.concat(line);
  }
}

При необходимости вы можете добавить другие проверки регулярных выражений для почтовых клиентов, которые оставляют разные цитируемые текстовые подписи.

person Tomislav Nakic-Alfirevic    schedule 07.03.2010
comment
Мне нравится упрощенный подход. - person Amit Bens; 21.10.2012