QuotedPrintable解码
最近在修改一个开源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 }
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 }