eml文件格式解析就这么难嘛?

beginow 2008-05-28 08:01:17
这几天在google csdn上狂搜eml文件解析的C#或相关语言代码,一无所获!!! 难道这个文件解析就这么难嘛?我想提出eml文件中的 from : to : cc: content: 等内容和处理乱码问题,用于asp.net中,肯求高人出来指点!!!!!!!狂谢不已!!!!!
...全文
4996 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
翔之光 2011-07-18
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 wangdj2 的回复:]

和大家分享一个解决方案,使用这种方法,可以非常容易读取和"EML"邮件相关的所有信息

1、添加COM组件cdosys.dll的引用,如图


2、相关代码
/// <summary>
/// 获取eml文件的主体内容
/// </summary>
/// <param name="file">eml文件的路径</param>……
[/Quote]

正解
chen435370738 2010-11-11
  • 打赏
  • 举报
回复
wangdj2 2010-09-17
  • 打赏
  • 举报
回复
和大家分享一个解决方案,使用这种方法,可以非常容易读取和"EML"邮件相关的所有信息

1、添加COM组件cdosys.dll的引用,如图


2、相关代码
/// <summary>
/// 获取eml文件的主体内容
/// </summary>
/// <param name="file">eml文件的路径</param>
/// <returns>eml文件的主体内容</returns>
public string ReadEML(string file)
{
CDO.Message oMsg = new CDO.Message();
ADODB.Stream stm = null;
//读取EML文件到CDO.MESSAGE,做分析的话,实际是用了下面的部分
try
{
stm = new ADODB.Stream();
stm.Open(System.Reflection.Missing.Value,
ADODB.ConnectModeEnum.adModeUnknown,
ADODB.StreamOpenOptionsEnum.adOpenStreamUnspecified,
"", "");
stm.Type = ADODB.StreamTypeEnum.adTypeBinary;//二进制方式读入

stm.LoadFromFile(file); //将EML读入数据流

oMsg.DataSource.OpenObject(stm, "_stream"); //将EML数据流载入到CDO.Message,要做解析的话,后面就可以了。

}
catch (IOException ex)
{

}
finally
{
stm.Close();
}
return oMsg.HTMLBody;//oMsg里包含了邮件相关的所有信息
}

有关“cdosys.dll”模块可以到网上进一步了解,使用它可以方面的完成邮件的所有功能
wedojava 2009-03-17
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 Navymk 的回复:]
分开邮件的各个部分,应该是用正则了.分开之后,对内容做读取的时候,需要用base64和qp两种解码,要根据邮件相关信息来确定具体用哪一种.

一般邮件获取内容编码方式的正则的写法应该是下面这个样子
C# codeprivateconststringencodingReg="(?<=(Content\\-Transfer\\-Encoding\\:)).*";
做一个TextAnalyze方法来统筹分析
C# code//c 内容//charset,文字编码方式,utf-8/gb2312等等//encoding,加密编码方式,包括qp和base64两种//…
[/Quote]
QDecode(Encoding.GetEncoding(charset), c)相关的方法我看到了,学习下,谢谢,但是
DecodeBase64(charset, encoding);对应的方法大概应该怎么写啊,我碰到了和楼主同样的问题,我主要是要提取出任何EML文件的内容,
我的编程环境是VS2005,语言是C#.
谢谢了.
w_dalu 2009-03-06
  • 打赏
  • 举报
回复
我这里有关邮件格式分析的文档,对你肯定有用。
第17卷第7期
2007年7月
计算机技术与发展
COMPUFER TECHNOI )( Y AND DEVEIDPMENT
V0l l7 NO.7
Ju1. 2007
EML格式解析及其访问实现
王倩倩,严莉莉,张燕平
(安徽大学计算智能与信号处理重点实验室,安徽合肥230039)
摘要:EML格式是各类电子邮件软件中所支持的一种通用格式,遵循RFC822及其后续扩展。文中对EML格式做了细
致的分析,在此基础上.使用VC作为开发1具,实现了Windows平台下对EML文件中各类相关信息的读取和解码。所实
现的CMail类应用于实际的项目开发中,取得了比较好的效果,能够满足一般用户的需求。用户也可根据自己的需求增加
相应的处理函数,同时文中的分析过程列其它系统平台下的类似需求具有一定的指导作用。
关键词:E ;多用途互联闻邮件扩展;RF 046;Bas~4
中图分类号:rrI]393.098 文献标识码:A 文章编号:1673—629X(2007)07—0067—03

Analysis and Access Implementation 0f EM L
WANG Qian—qian,YAN Li—li,ZHANG Yan—ping
(Ministry of l~ucation I<ey Lab.of Intelligent Computing& Signal Proce~ing,Anhui Univ.,Hefei 230039,China)
Abstract:EMI is a kind of general fommt supported by all kinds of Email software and this format follows the standard of RFC=822 and
its extensions.Analyzes the fore,at of EM L and then implements.how to read and decode the inform ation in EML un der W indows plat—
form withVC6.Designa cla~Ks namedCMai1and Llseitinthedevelopmentof project.ThT。ughtest.it achieves agood effectand can satis—
fy the needs of rnus:users.Users can also add Some functions according to tlleir o、、.Tl needs.The developers who have simiJar needs in
other platform call benefit fmm the an alysis in this paper.
Key words:EML;MIhiE;RF(2046:Base64
O 引 言
EML格式是微软公司在Outlook中所使用的一种
遵循RFC822及其后续扩展的文件格式,并成为各类
电子邮件软件的通用格式。深入了解EML格式,是
进行邮件客户端设计、垃圾邮件分析过滤等关于电子
邮件方面开发和研究的必要前提。文中对EML格式
做了细致的分析,在VC下设计实现了一个对EML格
式进行分析处理的类并应用于实际的项目开发中,取
得了理想的效果。
l 邮件结构
为使电子邮件在各种网络和服务器间正常地发送
和接收,人们对电子邮件的格式进行了规定。最初的
收稿日期:2006—09~18
基金项目:“几七互”计划国家重点肇础研究(2004CB318108);国家
自然科学基金资助项目(60475017,6013501(I);安徽省自然科学基
金资助项目(050420208)
作者简介:王倩倩(1982一),女,安徽六安人,硕士研究生,研究万向
为计算智能;张燕平,教授,硕士生导师,研究方向为人工神经网络、
智能算法及其应.}{j。
标准RFC822 Elj规定了电子邮件的一些基本规范,但
只能用于传输文本信息,无法满足用户的需求和网络
技术的发展。因此人们对电子邮件格式陆续增添了新
的内容,即MI (多用途互联网邮件扩展),其基本内
容定义于RFC2045~2049,文中主要讨论RFC'2045[2』
和RFC2046[3 J。
1.1 邮件类结构分析和总体设计
RFC中定义的邮件结构包括两个部分,即邮件头
和邮件体,两者由一个空行隔开。邮件头包括几个部
分:主题、创建者、收信人以及邮件创建日期等。为传
递多媒体信息,邮件头又增加了邮件体内容类型、邮件
体内容传输编码方式等。邮件体部分包括正文和附
件,结构较复杂,将在第三部分中详细介绍。
根据以上分析,可以设计一个CMail类,用于打
开、分析和处理EML文件,并返回用户所需的信息,
文中所涉及到的一些主要成员函数如下:
bool OpenMail()//渎人邮件文件
C,String GetFrom()//gy.得邮件创建者
CString GetSubject()//g~.得邮件主题
C~qtring GetContent()/ 得邮件内容
维普资讯 http://www.cqvip.com
· 68· 计算机技术与发展 第l7卷
CString Base64一Decode(C~String S)//Base64编码
的解码
CString GetBoundary(CString S)//渎取邮件文本
的边界定界符
CString GetTransfe~.xting(CString S)/ 取邮件
体传输的编码方式
CString GetContentType(CString S)/ 取邮件体
内容的类型
1.2 邮件头相关函数的实现
邮件头中各字段由一或多行文字组成,多行字段
的附加行以一个空格作为开始。各字段由字段名、可
选的空格、冒号、可选的注解空格、可选的字体段组成。
如某Content—Type字段描述为:Contenl~一Type:mul—
tipart/mixed;boundary=“= == = = == = = = = =
= = =0041883896= = ”。
实现中将邮件读入一个CString字符串中。对于
邮件头部的字段,使用CString提供的Find方法查找
各字段的位置,从该字段的冒号开始直到该字段结束
为该字段的字段体部分。字段结束标志为找到CRLF
结束符且下一行不以空格开始。例如对邮件头的
Subject字段: ’
hat where 0;
while(Mail.Find(“SuNect;”,where)!=一1);
tif(Mail.GetAt(where一1):‘\n’)break~
else where+ + :
}/馇找Mail中SuN~t位置,且查到的SuNect是一行的开

int end=where;//end为SuNect字段结束的位置
while((Mail.GetAt(end)!=‘\r’)&&(Mail.GetAt(end+
2)!=一)//7段结束标志
end+ + ;
CString subject=Mail.Mid(where+8,end—where一8);//
suNect为所需的字段体
字段体内容可能是纯文本也可能进行了编码,如
Subject字段,其字段体部分可能是如下形式:“:?
GB23127 B? fM3GvPa49rrcsru07b)(EsK5fX+ lfzfi4 +
MTjLSlie~-Ai7tLSsv0=?=”,此时需要进行解码。字
段体中,以“=?”开始,以“?=”结束,中间以“?”分
隔[引。第一部分指明字符集,如GB2312,第二部分为
编码方式,本例中的B代表Base64编码,最后一部分
为具体的主题内容。根据前一步取出的字段体内容,
通过在其字符串中查找“?”将几部分分别取出,根据其
提供的字符集和编码方式,使用后文所述的解码函数
获得文本内容。
其它与邮件头相关的函数都可以使用类似的方法
实现。
1.3 邮件体相关函数的实现
MIME中的邮件体部分比早期的单文本文件复杂
很多,实现时要根据其Content—Type类型决定具体
访问方法。Content—Type一般包括文本的类型、文本
使用的字符集等,文本为复合类型时还包括分隔不同
部分的分界字符串(boundary)。
RFC2046定义Content—Type顶层有5种离散类
型和两种复合类型。离散类型是text(文本信息),im—
age(图像信息),audio(音频信息),video(视频信息),
application(其它信息)。复合类型是multipart(多部分
信息)和message(压缩信息),顶层类型又有其子类型。
正常邮件中,正文文本多是text类型,其它类型大多作
为附件方式存在。由于邮件中可能多种类型并存,且
不同复合类型会相互嵌套,造成了邮件体分析时的复
杂情况。
1)当邮件体中只出现某种离散的顶层类型时。比
如邮件的Content—Type类型为Text/plain[ ,Text是
顶层类型,说明邮件体为文本类型,plain是Text的一
种子类型:纯文本类型。只需要读取邮件头部分的
Content—transfer~Encoding字段,来判断该邮件体部
分(可以根据邮件头和邮件体部分以空行分开这个特
点来提取出邮件体)是否采用了某种编码方式。如果
没有编码,可以在GetContent()中直接返回前面已经
得到的content值,否则调用需要的编码程序,将解码
后的字符串返回。
2)当邮件中出现复合的顶层类型时,情况就比较
复杂,下面以较常见的Multipart为例进行说明。在
Mtdtipart类型中,有两个常见子类Mixed,Alternative。
Mixed类型说明文本内容有多个部分,并且各部分之
间是有次序的,那么在分析邮件体的时候,必须考虑到
其内容的顺序,按其原先出现的顺序来提取出邮件体
内容。而Alternative说明文本是以多种类型出现的相
同内容,接收者可以根据用户的需要来提取所需要的
类型或者默认将所有的类型均显示出来。邮件体中各
个部分之间以boundary分隔。
boundary是边界定界符,一般由两个连字符‘一’
(0x2D)和紧跟着从邮件头Content—Type中取来的参
数值组成(如1.2中所述),用于分隔邮件体内容的不
同的各个部分。
在分析邮件体的正文文本时(此处以邮件中出现
的是Multipart的Mixed和Alternative两类为例,若要
实现其它复合类及其子类可以相似的方式实现),可在
GetContent()中调用一个递归函数。其具体程序流程
如下:
CString C,etContentl(C&ring s)//s为邮件字符串
维普资讯 http://www.cqvip.com
第7期 王倩倩等:EML格式解析及其访问实现 ·69 ·
{若s为空,则返回NULL;
读出s中的Content—Type类型;
if(s为离散类型)
{如果不是用户需要显示的类型,则返回NUL L;如果
是,则根据邮件正文与邮件头以空行分开的特点,使用s.Find
(“\r\n\r\n”)来找出正文的开头,将正文提取出来赋值给
sn;
根据邮件头中的Contem—transfer—coding的内容,来
判断邮件使用何种编码方式,使用相应的解码程序来还原字符
串Sn,还原为content字符串;
返回还原的正文;}
else{//s为复合类型情况
判断复合类型的子类型为何种情况;
根据邮件头中的boundary,读出邮件正文中的边界字
符串;
同上操作提取出邮件体部分; .
case(Mixed)://当其子类型为多个有次序的部分时;
{
用户所需要的类型置为all;/伽说明在读人邮件时将
所有内容都读人}
caSe(Alternative)://当其子类型为多个形式的单一类
型时:
{
读人用户需要的类型标识,若无标识,则默认用户需要
显示所有的类型,则用户所需要类型也置为all;}
根据boundary,将s分为第一个部分和余下部分
分别赋值给sfirst,srest;
content= GetContentl (sfirst) + GetContentl
(srest);}}
同样,在提取邮件体中的附件的时候,程序框架同
上述的提取邮件体的正文文本的程序基本相同,只是
在s为离散类型的情况的开头加入如下判断:
读入Content—Distx)sition的内容,如果其放置位
置为附件,即Content—Disposition的内容是attachment
时才继续进行下面的操作,否则返回NULL。
2 邮件编码格式
目前,MIME标准是电子邮件体编码遵循的标准,
大部分的邮件采用的都是MIME中规定的两种编码
格式B-ase64和QP(Quoted—Printable)。
2.1 Base64编码
Base64编码适用于不可读的二进制文件,如中文
文档等。编码方法是将原始字符串每三个字符放入一
个24位缓冲区并等分为4份,高位在先,根据每6位
所对应的数字的大小,用A~z,a~Z,0~9,+,供64
个字符重新表示。若编码后的位数不是4的整数倍,
在最后以“=”来填充。如字符串“hello”的二进制流
为:“O110100001100101011011000110110001101111”,
6位一组并补零后得到:“011010,000110,010101,
101100.011011,000110,111100”(a,G,V,S,b,G,8),
则其Base64编码为“aGVsbG8=”,据此可得到相应的
解码流程l引。
2.2 QP编码
QP编码主要适用于包含大量7位ASCII码的多
媒体邮件。QP编码方法是对于7位可打印的ASCII
码数据,编码后的数据保持不变,对于非ASCII码的
8bit字节数据,每个字节用等号及其十六进制表示。
如“尊敬”,各字的GB2312码是 F0,BEB4,则其对应
的QP编码为:“=D7=F0=BE=134”,容易设计出解
码算法。
2.3 字符集的讨论
文中讨论时使用的字符集为GB2312,实际应用中
邮件标题和内容中所使用的字符集可能还包括其它类
型,如BIG5等,此时需要使用相应的算法进行解码,
文中不再赘述。
3 结 论
通过以上讨论,可设计出CMail类用于EML邮件
的访问并用于实际的项目开发和研究中,取得了满意
的效果。根据具体需求增加相应的成员函数即可方便
地扩充程序功能。
参考文献:
[1] Crocker D H.Standard for The Format of APRA Internet
Text Messages[S/OL].1982.http://rfc.net/rfc0822.htm1.
[2] Freed N,Borenstein N.Multipurpose Imemet Mail Exten—
sions(MIME)Part One:Format of Intemet Message Bodies
[S/OL].1996.http://rfc.net/rfc2045.htm1.
[3] Freed N,BorensteinN.Multipurpose Intemet Mail Extensions
(MIME)Part Two:Media Types[S/OL].1996.http://
rfc.net/rfc2046.htm1.
[4] 曹建文,黄志平,魏新莉.基于MIME的电子邮件发送程序
的设计和实现[J].中南林学院学报,2004,24(5):108—
112.
[5] 王淑蓉,沈虹.分析MIME 邮件组成结构及构建邮件收
发系统[J].现代电子技术,2004(23):15—17.
[6] 陈训逊,方滨兴,李蕾.MIME 解码算法优化问题研究
[J].计算机应用,2003,23(12):263—265.
维普资讯 http://www.cqvip.com
嗷嗷叫的老马 2008-05-29
  • 打赏
  • 举报
回复
貌似不是很高难度的东东
yagebu1983 2008-05-29
  • 打赏
  • 举报
回复
学习+关注!!
帮你顶!!
beginow 2008-05-29
  • 打赏
  • 举报
回复
NAVYMK能不能给我一份完整的解析邮件代码,谢谢!
beginow 2008-05-29
  • 打赏
  • 举报
回复
谢谢楼上的朋友们,马上研究一下!谁有好的办法继续跟帖,狂谢!
可西哥 2008-05-29
  • 打赏
  • 举报
回复
去查一下EMail协议 MFC相关内容,编码原理都比较简单
Navymk 2008-05-28
  • 打赏
  • 举报
回复
建议你查阅一些邮件传输协议方面的白皮书
Navymk 2008-05-28
  • 打赏
  • 举报
回复
分开邮件的各个部分,应该是用正则了.分开之后,对内容做读取的时候,需要用base64和qp两种解码,要根据邮件相关信息来确定具体用哪一种.

一般邮件获取内容编码方式的正则的写法应该是下面这个样子

private const string encodingReg = "(?<=(Content\\-Transfer\\-Encoding\\:)).*";

做一个TextAnalyze方法来统筹分析

//c 内容
//charset,文字编码方式,utf-8/gb2312等等
//encoding,加密编码方式,包括qp和base64两种
//因为encoding获得的加密编码标志可能不同,所以用了switch.
private string TextAnalyze(string c, string charset, string encoding)
{
switch (encoding.ToLower())
{
case "quoted-printable":
case "qp":
case "q":
try { c = QDecode(Encoding.GetEncoding(charset), c); }
catch (Exception e) { throw new Exception("TextAnalyze(quoted-printabl)" + e.Message); }
break;
case "base64":
case "b":
try
{ c = DecodeBase64(charset, encoding); }
catch (Exception e) { throw new Exception("TextAnalyze(DecodeBase64)" + e.Message); }
break;
}
return c;
}

base64在c#中处理很简单了,就放一个qp的解码.这个也是在网上辛苦搜到的.当时自己水平太凹,总编不好.

/// <summary>
/// quoted-printable解码程序.
/// </summary>
/// <param name="encoding">解码目标字符集</param>
/// <param name="data">需要解码的字符串</param>
/// <returns></returns>
private static string QDecode(System.Text.Encoding encoding, string data)
{
MemoryStream strm = new MemoryStream(System.Text.Encoding.Default.GetBytes(data));
int b = strm.ReadByte();
MemoryStream dStrm = new MemoryStream();
while (b > -1)
{
// Hex eg. =E4
if (b == '=')
{
byte[] buf = new byte[2];
strm.Read(buf, 0, 2);
if (!(buf[0] == '\r' && buf[1] == '\n'))
{
int val = int.Parse(System.Text.Encoding.Default.GetString(buf), System.Globalization.NumberStyles.HexNumber);
//int val = int.Parse(System.Text.Encoding.Default.GetString(buf));
byte[] temp = new Byte[] { (byte)val };
dStrm.Write(temp, 0, temp.Length);
}
}
else
{
string encodedChar = encoding.GetString(new byte[] { (byte)b });
byte[] d = System.Text.Encoding.Default.GetBytes(encodedChar);
dStrm.Write(d, 0, d.Length);
}

b = strm.ReadByte();
}
return encoding.GetString(dStrm.ToArray());
}

当时对我来讲最困难的就是qp这部分了,这部分解决之后其他的都很好办的.不知道你什么情况.
beginow 2008-05-28
  • 打赏
  • 举报
回复
谢谢楼上的,我看的eml文件里有的没编码,有的编码了,有的还有html标签,太乱了!如何区分base64和quoted-printable编码啊? base64 是不是放在?= ?=中间的!
  • 打赏
  • 举报
回复
呃,这个代码C的比较多一些.
再说了eml格式比较简单,自己用正则匹配一下就能拿到相关的字段.
剩下的就是解码了.常见的也就是base64 和quoted-printable

110,546

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧