Как удалить цитируемый текст из электронного письма и показывать только новый текст - PullRequest
13 голосов
/ 05 марта 2010

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

Как правило, вы увидите это:

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

This is the first email

2-е письмо (ответьте первому)

This is the second email

Tim said:
This is the first email

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

Ответы [ 6 ]

12 голосов
/ 08 июля 2010

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

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

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

5 голосов
/ 09 августа 2013

Проверьте патент Google на это: http://www.google.com/patents/US7222299

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

2 голосов
/ 11 апреля 2011

RegEx работает нормально, за исключением того, что он соответствует тексту, который начинается с Subject и игнорирует все, что идет перед "Subject"

Text
-------- Original Message -------- 
<TABLE border="0" cellpadding="0" cellspacing="0">
  <TBODY>
    <TR>
      <TH align="right" valign="baseline">
      // the matcher starts working from here
2 голосов
/ 05 марта 2010

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

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

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

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

НЕ ИСПЫТАНО и больше похоже на псевдокод

    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);
    }
1 голос
/ 07 марта 2010

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

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

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

1 голос
/ 05 марта 2010

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

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

    border-left: 1px solid #CCC; маржа: 0px 0px 0px 0.8ex; отступ слева: 1ex; таблица стилей агента пользователя BLOCKQUOTE

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

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

...