포함 할 수있는 인용 된 답장 텍스트에서 이메일 텍스트를 구문 분석하는 방법을 알아 내려고합니다. 나는 보통 이메일 클라이언트가 "그런 날짜에 그렇게 썼다"를 붙이거나 줄 앞에 꺾쇠 괄호를 붙인다는 것을 알아 챘다. 불행히도 모든 사람이 이렇게하는 것은 아닙니다. 응답 텍스트를 프로그래밍 방식으로 감지하는 방법에 대한 아이디어가있는 사람이 있습니까? 이 파서를 작성하기 위해 C #을 사용하고 있습니다.
포함 할 수있는 인용 된 답장 텍스트에서 이메일 텍스트를 구문 분석하는 방법을 알아 내려고합니다. 나는 보통 이메일 클라이언트가 "그런 날짜에 그렇게 썼다"를 붙이거나 줄 앞에 꺾쇠 괄호를 붙인다는 것을 알아 챘다. 불행히도 모든 사람이 이렇게하는 것은 아닙니다. 응답 텍스트를 프로그래밍 방식으로 감지하는 방법에 대한 아이디어가있는 사람이 있습니까? 이 파서를 작성하기 위해 C #을 사용하고 있습니다.
답변:
나는 이것에 대해 더 많은 검색을했고 여기에 내가 찾은 것이 있습니다. 기본적으로이 작업을 수행하는 두 가지 상황이 있습니다. 전체 스레드가있는 경우와없는 경우입니다. 이 두 가지 범주로 나눌 것입니다.
스레드가있는 경우 :
일련의 전체 이메일이있는 경우 제거중인 항목이 실제로 인용 된 텍스트라는 매우 높은 수준의 확신을 얻을 수 있습니다. 이를 수행하는 두 가지 방법이 있습니다. 첫째, 메시지의 Message-ID, In-Reply-To ID 및 Thread-Index를 사용하여 개별 메시지, 상위 메시지 및 해당 스레드를 확인할 수 있습니다. 이에 대한 자세한 내용은 RFC822 , RFC2822 , 스레딩에 대한 흥미로운 기사 또는 스레딩에 대한이 기사를 참조하십시오. . 스레드를 다시 어셈블하면 외부 텍스트 (예 : To, From, CC 등 ... 행)를 제거 할 수 있으며 완료됩니다.
작업중인 메시지에 헤더가없는 경우 유사성 일치를 사용하여 이메일의 어떤 부분이 회신 텍스트인지 확인할 수도 있습니다. 이 경우 반복되는 텍스트를 결정하기 위해 유사성 일치를 수행해야합니다. 이 경우 코드 프로젝트 또는 이 알고리즘 과 같은 Levenshtein Distance 알고리즘 을 살펴볼 수 있습니다 .
스레딩 프로세스에 관심이 있다면 이메일 스레드 재 조립에 대한이 훌륭한 PDF를 확인하십시오 .
스레드가없는 경우 :
스레드에서 단 하나의 메시지 만 갇힌 경우 인용문이 무엇인지 추측해야합니다. 이 경우 내가 본 다른 견적 방법은 다음과 같습니다.
거기에서 텍스트를 제거하면 완료됩니다. 이들 중 하나의 단점은 모두 발신자가 인용 된 텍스트 위에 답장을 넣고 인터리브하지 않았다고 가정한다는 것입니다 (인터넷의 이전 스타일처럼). 그럴 경우 행운을 빕니다. 나는 이것이 당신 중 일부를 돕기를 바랍니다!
우선 이것은 까다로운 작업입니다.
다른 전자 메일 클라이언트에서 일반적인 응답을 수집하고이를 구문 분석 할 올바른 정규식 (또는 기타)을 준비해야합니다. 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 :"텍스트를 찾아 문제를 해결했습니다. 그러나 사용자가이 텍스트를 삭제하거나 이메일 하단에 답장하면 많은 사람들이하는 것처럼이 솔루션은 작동하지 않습니다.
마찬가지로 이메일 클라이언트가 다른 날짜 문자열을 사용하거나 날짜 문자열을 포함하지 않으면 정규식이 실패합니다.
-- Please reply above this line. DO NOT REMOVE IT! --
입니다. 또한 내가 경험 한 것은 일부 이메일 클라이언트 xxx wrote on <datetime>:
가 전체 견적 앞과 그 라인 앞에 한 줄을 추가하기 때문에 항상 작동하지 않는다는 것입니다 . 이 줄은 정규식으로 구문 분석 할 수 있지만 이메일 클라이언트가 다르기 때문에 언어와 형식이 다를 수 있습니다.
전자 메일에는 회신에 대한 보편적 인 표시가 없습니다. 할 수있는 최선의 방법은 가장 일반적인 패턴을 포착하고 새로운 패턴을 발견 할 때 파싱하는 것입니다.
어떤 사람들은 인용 된 텍스트 안에 답글을 삽입하므로 (예를 들어 내 상사가 내가 질문 한 것과 같은 줄에 질문에 답함) 어떤 일을하더라도 보관하고 싶었던 정보를 잃을 수도 있습니다.
다음은 @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();
}
이것은 좋은 해결책입니다. 너무 오래 찾아서 찾았습니다.
위에서 언급했듯이 한 가지 추가 사항은 대소 문자가 현명하기 때문에 위의 표현은 내 gmail 및 outlook (2010) 응답을 올바르게 구문 분석하지 않았으므로 다음 두 개의 Regex를 추가했습니다. 문제가 있으면 알려주세요.
//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),
건배
그러나 github 에 답장을 추출 하는 Ruby lib 가 있다는 것을 알고 있는지 확실하지 않습니다 . .NET을 사용하는 경우 https://github.com/EricJWHuang/EmailReplyParser에 .NET이 있습니다.
SigParser.com 의 API 를 사용 하는 경우 단일 이메일 텍스트 문자열에서 회신 체인에있는 모든 분리 된 이메일의 배열을 제공합니다. 따라서 10 개의 이메일이있는 경우 10 개의 이메일 모두에 대한 텍스트를 받게됩니다.
여기에서 자세한 API 사양을 볼 수 있습니다.