C#:用于解码Quoted-Printable编码的类?

C#中是否存在可以将Quoted-Printable编码转换为String的现有类? 单击上面的链接以获取有关编码的更多信息。

为方便起见,以上链接引用了以下内容。

任何8位字节值可以用3个字符编码,“=”后跟两个hex数字(0-9或A-F),表示字节的数值。 例如,US-ASCII换页符(十进制值12)可以用“= 0C”表示,US-ASCII等号(十进制值61)用“= 3D”表示。 除了可打印的ASCII字符或行尾字符之外的所有字符都必须以这种方式编码。

所有可打印的ASCII字符(33到126之间的十进制值)可以由它们自己表示,除了“=”(十进制61)。

ASCII选项卡和空格字符,十进制值9和32,可以由它们自己表示,除非这些字符出现在一行的末尾。 如果其中一个字符出现在一行的末尾,则必须将其编码为“= 09”(制表符)或“= 20”(空格)。

如果正在编码的数据包含有意义的换行符,则必须将它们编码为ASCII CR LF序列,而不是其原始字节值。 相反,如果字节值13和10具有除行尾之外的含义,则它们必须被编码为= 0D和= 0A。

引用可打印编码数据的行不得超过76个字符。 为了在不改变编码文本的情况下满足该要求,可以根据需要添加软换行符。 软换行符在编码行的末尾包含“=”,并且不会在解码的文本中导致换行符。

框架库中有一些function可以做到这一点,但它似乎没有干净利落。 该实现位于内部类System.Net.Mime.QuotedPrintableStream 。 这个类定义了一个名为DecodeBytes的方法,它DecodeBytes你的需要。 该方法似乎仅由一种用于解码MIME标头的方法使用。 此方法也是内部的,但在几个地方相当直接调用,例如Attachment.Name setter。 示范:

 using System; using System.Net.Mail; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Attachment attachment = Attachment.CreateAttachmentFromString("", "=?iso-8859-1?Q?=A1Hola,_se=F1or!?="); Console.WriteLine(attachment.Name); } } } 

产生输出:

¡HOLA,_señor!

您可能必须进行一些测试以确保正确处理回车等,尽管我们似乎做了一个快速测试。 但是,依赖此function可能并不明智,除非您的用例足够接近解码MIME头字符串,您认为它不会被对库所做的任何更改破坏。 您可能最好编写自己的可引用打印解码器。

我扩展了Martin Murphy的解决方案,我希望它能适用于所有情况。

 private static string DecodeQuotedPrintables(string input, string charSet) { if (string.IsNullOrEmpty(charSet)) { var charSetOccurences = new Regex(@"=\?.*\?Q\?", RegexOptions.IgnoreCase); var charSetMatches = charSetOccurences.Matches(input); foreach (Match match in charSetMatches) { charSet = match.Groups[0].Value.Replace("=?", "").Replace("?Q?", ""); input = input.Replace(match.Groups[0].Value, "").Replace("?=", ""); } } Encoding enc = new ASCIIEncoding(); if (!string.IsNullOrEmpty(charSet)) { try { enc = Encoding.GetEncoding(charSet); } catch { enc = new ASCIIEncoding(); } } //decode iso-8859-[0-9] var occurences = new Regex(@"=[0-9A-Z]{2}", RegexOptions.Multiline); var matches = occurences.Matches(input); foreach (Match match in matches) { try { byte[] b = new byte[] { byte.Parse(match.Groups[0].Value.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier) }; char[] hexChar = enc.GetChars(b); input = input.Replace(match.Groups[0].Value, hexChar[0].ToString()); } catch { } } //decode base64String (utf-8?B?) occurences = new Regex(@"\?utf-8\?B\?.*\?", RegexOptions.IgnoreCase); matches = occurences.Matches(input); foreach (Match match in matches) { byte[] b = Convert.FromBase64String(match.Groups[0].Value.Replace("?utf-8?B?", "").Replace("?UTF-8?B?", "").Replace("?", "")); string temp = Encoding.UTF8.GetString(b); input = input.Replace(match.Groups[0].Value, temp); } input = input.Replace("=\r\n", ""); return input; } 

我写得很快。

  public static string DecodeQuotedPrintables(string input) { var occurences = new Regex(@"=[0-9A-H]{2}", RegexOptions.Multiline); var matches = occurences.Matches(input); var uniqueMatches = new HashSet(matches); foreach (string match in uniqueMatches) { char hexChar= (char) Convert.ToInt32(match.Substring(1), 16); input =input.Replace(match, hexChar.ToString()); } return input.Replace("=\r\n", ""); } 

如果使用UTF-8编码解码quoted-printable,则需要注意不能一次一个地解码每个引用的可打印序列,因为其他人已经显示是否同时存在引用的可打印字符。

例如 – 如果您有以下序列= E2 = 80 = 99并使用UTF8一次解码一次,则会得到三个“怪异”字符 – 如果您构建一个包含三个字节的数组并将三个字节转换为UTF8编码你得到一个aphostrope。

显然,如果您使用的是ASCII编码,那么一次一个就没有问题,但是解码运行意味着无论使用何种文本编码器,您的代码都能正常工作。

哦,别忘了= 3D是一个特殊情况,这意味着你需要再解码一下你所拥有的……这是一个疯狂的陷阱!

希望有所帮助

这个引用可打印的解码器工作得很好!

 public static byte[] FromHex(byte[] hexData) { if (hexData == null) { throw new ArgumentNullException("hexData"); } if (hexData.Length < 2 || (hexData.Length / (double)2 != Math.Floor(hexData.Length / (double)2))) { throw new Exception("Illegal hex data, hex data must be in two bytes pairs, for example: 0F,FF,A3,... ."); } MemoryStream retVal = new MemoryStream(hexData.Length / 2); // Loop hex value pairs for (int i = 0; i < hexData.Length; i += 2) { byte[] hexPairInDecimal = new byte[2]; // We need to convert hex char to decimal number, for example F = 15 for (int h = 0; h < 2; h++) { if (((char)hexData[i + h]) == '0') { hexPairInDecimal[h] = 0; } else if (((char)hexData[i + h]) == '1') { hexPairInDecimal[h] = 1; } else if (((char)hexData[i + h]) == '2') { hexPairInDecimal[h] = 2; } else if (((char)hexData[i + h]) == '3') { hexPairInDecimal[h] = 3; } else if (((char)hexData[i + h]) == '4') { hexPairInDecimal[h] = 4; } else if (((char)hexData[i + h]) == '5') { hexPairInDecimal[h] = 5; } else if (((char)hexData[i + h]) == '6') { hexPairInDecimal[h] = 6; } else if (((char)hexData[i + h]) == '7') { hexPairInDecimal[h] = 7; } else if (((char)hexData[i + h]) == '8') { hexPairInDecimal[h] = 8; } else if (((char)hexData[i + h]) == '9') { hexPairInDecimal[h] = 9; } else if (((char)hexData[i + h]) == 'A' || ((char)hexData[i + h]) == 'a') { hexPairInDecimal[h] = 10; } else if (((char)hexData[i + h]) == 'B' || ((char)hexData[i + h]) == 'b') { hexPairInDecimal[h] = 11; } else if (((char)hexData[i + h]) == 'C' || ((char)hexData[i + h]) == 'c') { hexPairInDecimal[h] = 12; } else if (((char)hexData[i + h]) == 'D' || ((char)hexData[i + h]) == 'd') { hexPairInDecimal[h] = 13; } else if (((char)hexData[i + h]) == 'E' || ((char)hexData[i + h]) == 'e') { hexPairInDecimal[h] = 14; } else if (((char)hexData[i + h]) == 'F' || ((char)hexData[i + h]) == 'f') { hexPairInDecimal[h] = 15; } } // Join hex 4 bit(left hex cahr) + 4bit(right hex char) in bytes 8 it retVal.WriteByte((byte)((hexPairInDecimal[0] << 4) | hexPairInDecimal[1])); } return retVal.ToArray(); } public static byte[] QuotedPrintableDecode(byte[] data) { if (data == null) { throw new ArgumentNullException("data"); } MemoryStream msRetVal = new MemoryStream(); MemoryStream msSourceStream = new MemoryStream(data); int b = msSourceStream.ReadByte(); while (b > -1) { // Encoded 8-bit byte(=XX) or soft line break(=CRLF) if (b == '=') { byte[] buffer = new byte[2]; int nCount = msSourceStream.Read(buffer, 0, 2); if (nCount == 2) { // Soft line break, line splitted, just skip CRLF if (buffer[0] == '\r' && buffer[1] == '\n') { } // This must be encoded 8-bit byte else { try { msRetVal.Write(FromHex(buffer), 0, 1); } catch { // Illegal value after =, just leave it as is msRetVal.WriteByte((byte)'='); msRetVal.Write(buffer, 0, 2); } } } // Illegal =, just leave as it is else { msRetVal.Write(buffer, 0, nCount); } } // Just write back all other bytes else { msRetVal.WriteByte((byte)b); } // Read next byte b = msSourceStream.ReadByte(); } return msRetVal.ToArray(); } 
  private string quotedprintable(string data, string encoding) { data = data.Replace("=\r\n", ""); for (int position = -1; (position = data.IndexOf("=", position + 1)) != -1;) { string leftpart = data.Substring(0, position); System.Collections.ArrayList hex = new System.Collections.ArrayList(); hex.Add(data.Substring(1 + position, 2)); while (position + 3 < data.Length && data.Substring(position + 3, 1) == "=") { position = position + 3; hex.Add(data.Substring(1 + position, 2)); } byte[] bytes = new byte[hex.Count]; for (int i = 0; i < hex.Count; i++) { bytes[i] = System.Convert.ToByte(new string(((string)hex[i]).ToCharArray()), 16); } string equivalent = System.Text.Encoding.GetEncoding(encoding).GetString(bytes); string rightpart = data.Substring(position + 3); data = leftpart + equivalent + rightpart; } return data; } 

我一直在寻找一个动态的解决方案,花了两天时间尝试不同的解决方 此解决方案将支持日语字符和其他标准字符集

 private static string Decode(string input, string bodycharset) { var i = 0; var output = new List(); while (i < input.Length) { if (input[i] == '=' && input[i + 1] == '\r' && input[i + 2] == '\n') { //Skip i += 3; } else if (input[i] == '=') { string sHex = input; sHex = sHex.Substring(i + 1, 2); int hex = Convert.ToInt32(sHex, 16); byte b = Convert.ToByte(hex); output.Add(b); i += 3; } else { output.Add((byte)input[i]); i++; } } if (String.IsNullOrEmpty(bodycharset)) return Encoding.UTF8.GetString(output.ToArray()); else { if (String.Compare(bodycharset, "ISO-2022-JP", true) == 0) return Encoding.GetEncoding("Shift_JIS").GetString(output.ToArray()); else return Encoding.GetEncoding(bodycharset).GetString(output.ToArray()); } } 

然后你可以调用函数

 Decode("=E3=82=AB=E3=82=B9=E3", "utf-8") 

这最初是在这里找到的

唯一一个适合我的人。

http://sourceforge.net/apps/trac/syncmldotnet/wiki/Quoted%20Printable

如果您只需要解码QP,请从上面的链接中获取代码中的三个函数:

  HexDecoderEvaluator(Match m) HexDecoder(string line) Decode(string encodedText) 

然后就是:

 var humanReadable = Decode(myQPString); 

请享用

更好的方案

  private static string DecodeQuotedPrintables(string input, string charSet) { try { enc = Encoding.GetEncoding(CharSet); } catch { enc = new UTF8Encoding(); } var occurences = new Regex(@"(=[0-9A-Z]{2}){1,}", RegexOptions.Multiline); var matches = occurences.Matches(input); foreach (Match match in matches) { try { byte[] b = new byte[match.Groups[0].Value.Length / 3]; for (int i = 0; i < match.Groups[0].Value.Length / 3; i++) { b[i] = byte.Parse(match.Groups[0].Value.Substring(i * 3 + 1, 2), System.Globalization.NumberStyles.AllowHexSpecifier); } char[] hexChar = enc.GetChars(b); input = input.Replace(match.Groups[0].Value, hexChar[0].ToString()); } catch { ;} } input = input.Replace("=\r\n", "").Replace("=\n", "").Replace("?=", ""); return input; } 
 public static string DecodeQuotedPrintables(string input, Encoding encoding) { var regex = new Regex(@"\=(?[0-9A-Z]{2})", RegexOptions.Multiline); var matches = regex.Matches(input); var bytes = new byte[matches.Count]; for (var i = 0; i < matches.Count; i++) { bytes[i] = Convert.ToByte(matches[i].Groups["Symbol"].Value, 16); } return encoding.GetString(bytes); } 

有时,字符串到EML文件中由几个编码部分组成。 这是在这些情况下使用Dave方法的函数:

 public string DecodeQP(string codedstring) { Regex codified; codified=new Regex(@"=\?((?!\?=).)*\?=", RegexOptions.IgnoreCase); MatchCollection setMatches = codified.Matches(cadena); if(setMatches.Count > 0) { Attachment attdecode; codedstring= ""; foreach (Match match in setMatches) { attdecode = Attachment.CreateAttachmentFromString("", match.Value); codedstring+= attdecode.Name; } } return codedstring; } 

请注意:带有“input.Replace”的解决方案遍布Internet,但仍然不正确。

请参阅,如果您有一个解码符号然后使用“替换” ,则“输入”中的所有符号将被替换,然后所有后续解码将被破坏。

更正确的解决方案

 public static string DecodeQuotedPrintable(string input, string charSet) { Encoding enc; try { enc = Encoding.GetEncoding(charSet); } catch { enc = new UTF8Encoding(); } input = input.Replace("=\r\n=", "="); input = input.Replace("=\r\n ", "\r\n "); input = input.Replace("= \r\n", " \r\n"); var occurences = new Regex(@"(=[0-9A-Z]{2})", RegexOptions.Multiline); //{1,} var matches = occurences.Matches(input); foreach (Match match in matches) { try { byte[] b = new byte[match.Groups[0].Value.Length / 3]; for (int i = 0; i < match.Groups[0].Value.Length / 3; i++) { b[i] = byte.Parse(match.Groups[0].Value.Substring(i * 3 + 1, 2), System.Globalization.NumberStyles.AllowHexSpecifier); } char[] hexChar = enc.GetChars(b); input = input.Replace(match.Groups[0].Value, new String(hexChar)); } catch { Console.WriteLine("QP dec err"); } } input = input.Replace("?=", ""); //.Replace("\r\n", ""); return input; }