最近在修改一个开源POP3客户端框架时,碰到了QuotedPrintable乱码的问题,原框架是老外写的,没有考虑到中文的情况。因此在对QuotedPrintable编码的内容进行解码时,是逐个字符进行转换的,英文的当然不会有任何问题。于是在网上搜了一下,发现基于C#的QuotedPrintable解码的代码很少,找到的大部分都是转帖,而且大部分都是采用默认的Encoding进行解码,前提是用了他们自己写的编码方式才能解码。这当然不符合我的要求,既然作为POP3客户端,收到的邮件都是来自不同的服务器,编码方式是动态变化的。

于是只能自己动手去解决,在对原始邮件内容(QuotedPrintable加密的内容)解析时,碰到的主要问题是一个中文字符由多个字节组成,如果字节取得不正确或没有取完整,那么只能解码一部分,会出现个别文字乱码的情况。网上提供的清一色代码都是用字符串截取的方式,但是这种方式不够保险,总会出现截取不完整的情况,或者完全失效。参考开源POP3客户端框架作者采用的正则匹配替换方式,采用替换的方式最为保险。

基本实现方式是,匹配出多个QuotedPrintable编码的内容,比如得到=4F=B5=9F=AB,然后再分解得到单个编码内容如=4F,这样为一个字节的内容,将多个字节放在一个数组,得到一个完整的byte[],接下来就是用对应的编码方式getstring即可。用得到的string替换编码内容,达到解码的方式。

QuotedPrintable解码实现

 1         private const string QpSinglePattern = (\\=([0-9A-F][0-9A-F]));
 2 
 3         private const string QpMutiplePattern = @”((\=[0-9A-F][0-9A-F])+=?\s*)+;
 4 
 5         public static string Decode(string contents, Encoding encoding)
 6         {
 7             if (contents == null)
 8             {
 9                 throw new ArgumentNullException(contents);
10             }
11 
12             // 替换被编码的内容
13             string result = Regex.Replace(contents, QpMutiplePattern, new MatchEvaluator(delegate(Match m)
14             {
15                 List<byte> buffer = new List<byte>();
16                 // 把匹配得到的多行内容逐个匹配得到后转换成byte数组
17                 MatchCollection matches = Regex.Matches(m.Value, QpSinglePattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
18                 foreach (Match match in matches)
19                 {
20                     buffer.Add((byte)HexToByte(match.Groups[2].Value.Trim()));
21                 }
22                 return encoding.GetString(buffer.ToArray());
23             }), RegexOptions.IgnoreCase | RegexOptions.Compiled);
24 
25             // 替换多余的链接=号
26             result = Regex.Replace(result, @”=\s+“”);
27 
28             return result;
29         }
30 
31         private static int HexToByte(string hex)
32         {
33             int num1 = 0;
34             string text1 = 0123456789ABCDEF;
35             for (int num2 = 0; num2 < hex.Length; num2++)
36             {
37                 if (text1.IndexOf(hex[num2]) == 1)
38                 {
39                     return 1;
40                 }
41                 num1 = (num1 * 0x10+ text1.IndexOf(hex[num2]);
42             }
43             return num1;
44         }