포함 할 수있는 인용 된 답장 텍스트에서 이메일 텍스트를 구문 분석하는 방법을 알아 내려고합니다. 나는 보통 이메일 클라이언트가 “그런 날짜에 그렇게 썼다”를 붙이거나 줄 앞에 꺾쇠 괄호를 붙인다는 것을 알아 챘다. 불행히도 모든 사람이 이렇게하는 것은 아닙니다. 응답 텍스트를 프로그래밍 방식으로 감지하는 방법에 대한 아이디어가있는 사람이 있습니까? 이 파서를 작성하기 위해 C #을 사용하고 있습니다.
답변
나는 이것에 대해 더 많은 검색을했고 여기에 내가 찾은 것이 있습니다. 기본적으로이 작업을 수행하는 두 가지 상황이 있습니다. 전체 스레드가있는 경우와없는 경우입니다. 이 두 가지 범주로 나눌 것입니다.
스레드가있는 경우 :
일련의 전체 이메일이있는 경우 제거중인 항목이 실제로 인용 된 텍스트라는 매우 높은 수준의 확신을 얻을 수 있습니다. 이를 수행하는 두 가지 방법이 있습니다. 첫째, 메시지의 Message-ID, In-Reply-To ID 및 Thread-Index를 사용하여 개별 메시지, 상위 메시지 및 해당 스레드를 확인할 수 있습니다. 이에 대한 자세한 내용은 RFC822 , RFC2822 , 스레딩에 대한 흥미로운 기사 또는 스레딩에 대한이 기사를 참조하십시오. . 스레드를 다시 어셈블하면 외부 텍스트 (예 : To, From, CC 등 … 행)를 제거 할 수 있으며 완료됩니다.
작업중인 메시지에 헤더가없는 경우 유사성 일치를 사용하여 이메일의 어떤 부분이 회신 텍스트인지 확인할 수도 있습니다. 이 경우 반복되는 텍스트를 결정하기 위해 유사성 일치를 수행해야합니다. 이 경우 코드 프로젝트 또는 이 알고리즘 과 같은 Levenshtein Distance 알고리즘 을 살펴볼 수 있습니다 .
스레딩 프로세스에 관심이 있다면 이메일 스레드 재 조립에 대한이 훌륭한 PDF를 확인하십시오 .
스레드가없는 경우 :
스레드에서 단 하나의 메시지 만 갇힌 경우 인용문이 무엇인지 추측해야합니다. 이 경우 내가 본 다른 견적 방법은 다음과 같습니다.
- 선 (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);
결국 견적을 제거하려면 :
new Regex("^>.*$", RegexOptions.IgnoreCase | RegexOptions.Multiline);
다음은 테스트 응답의 작은 모음입니다 (샘플을 — 로 나눈 값 ).
From: test@test.com [mailto:test@test.com]
Sent: Tuesday, January 13, 2009 1:27 PM
----
2008/12/26 <test@test.com>
> text
----
test@test.com wrote:
> text
----
test@test.com wrote: text
text
----
2009/1/13 <test@test.com>
> text
----
test@test.com wrote: text
text
----
2009/1/13 <test@test.com>
> text
> text
----
2009/1/13 <test@test.com>
> text
> text
----
test@test.com wrote:
> text
> text
<response here>
----
--- On Fri, 23/1/09, test@test.com <test@test.com> wrote:
> text
> text
감사합니다, Oleg Yaroshevych
답변
정규식에 대해 Goleg, 감사합니다! 정말 도움이되었습니다. 이것은 C #이 아니지만 Google 직원을위한 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
지금까지 꽤 잘 작동했습니다.
답변
이를 수행하는 가장 쉬운 방법은 다음과 같은 콘텐츠에 마커를 배치하는 것입니다.
—이 줄 위에 답장 해주세요 —
의심의 여지없이, 인용 된 텍스트를 구문 분석하는 것은 다른 이메일 클라이언트가 다른 방식으로 텍스트를 인용하므로 사소한 작업이 아닙니다. 이 문제를 제대로 해결하려면 모든 이메일 클라이언트를 고려하고 테스트해야합니다.
Facebook은 이것을 할 수 있지만 프로젝트에 큰 예산이 없다면 아마 그렇게 할 수 없습니다.
Oleg는 정규식을 사용하여 “On 13 Jul 2012, at 13:09, xxx written :”텍스트를 찾아 문제를 해결했습니다. 그러나 사용자가이 텍스트를 삭제하거나 이메일 하단에 답장하면 많은 사람들이하는 것처럼이 솔루션은 작동하지 않습니다.
마찬가지로 이메일 클라이언트가 다른 날짜 문자열을 사용하거나 날짜 문자열을 포함하지 않으면 정규식이 실패합니다.
답변
전자 메일에는 회신에 대한 보편적 인 표시가 없습니다. 할 수있는 최선의 방법은 가장 일반적인 패턴을 포착하고 새로운 패턴을 발견 할 때 파싱하는 것입니다.
어떤 사람들은 인용 된 텍스트 안에 답글을 삽입하므로 (예를 들어 내 상사가 내가 질문 한 것과 같은 줄에 질문에 답함) 어떤 일을하더라도 보관하고 싶었던 정보를 잃을 수도 있습니다.
답변
다음은 @hurshagrawal의 Ruby 코드의 C # 버전입니다. 루비를 잘 모르기 때문에 꺼질 수 있지만 제대로 된 것 같습니다.
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();
}
답변
원본 메시지 (예 : 웹 응용 프로그램의 알림)를 제어하는 경우 고유하고 식별 가능한 헤더를 배치하고 원본 게시물의 구분자로 사용할 수 있습니다.