C #: class per la decodifica della codifica Quoted-Printable?

Esiste una class esistente in C # in grado di convertire la codifica Quoted-Printable in String ? Clicca sul link qui sopra per ottenere maggiori informazioni sulla codifica.

Quanto segue è citato dal link qui sopra per la vostra convenienza.

Qualsiasi valore byte a 8 bit può essere codificato con 3 caratteri, un “=” seguito da due cifre esadecimali (0-9 o A-F) che rappresentano il valore numerico del byte. Ad esempio, un carattere di avanzamento modulo US-ASCII (valore decimale 12) può essere rappresentato da “= 0C” e un segno di uguale US-ASCII (valore decimale 61) è rappresentato da “= 3D”. Tutti i caratteri tranne i caratteri ASCII stampabili o i caratteri di fine riga devono essere codificati in questo modo.

Tutti i caratteri ASCII stampabili (valori decimali compresi tra 33 e 126) possono essere rappresentati da soli, tranne “=” (decimale 61).

I caratteri tab e spazio ASCII, i valori decimali 9 e 32, possono essere rappresentati da soli, tranne se questi caratteri compaiono alla fine di una riga. Se uno di questi caratteri appare alla fine di una riga, deve essere codificato come “= 09” (scheda) o “= 20” (spazio).

Se i dati in fase di codifica contengono interruzioni di riga significative, devono essere codificati come una sequenza ASCII CR LF, non come valori di byte originali. Viceversa se i valori di byte 13 e 10 hanno significati diversi da quelli di fine riga allora devono essere codificati come = 0D e = 0A.

Le righe dei dati codificati e stampabili non devono superare i 76 caratteri. Per soddisfare questo requisito senza alterare il testo codificato, le interruzioni di linea morbida possono essere aggiunte come desiderato. Un’interruzione di linea morbida consiste in un “=” alla fine di una riga codificata e non causa un’interruzione di riga nel testo decodificato.

Esiste funzionalità nelle librerie del framework per fare ciò, ma non sembra essere chiaramente esposto. L’implementazione è nella class interna System.Net.Mime.QuotedPrintableStream . Questa class definisce un metodo chiamato DecodeBytes che fa ciò che vuoi. Il metodo sembra essere utilizzato da un solo metodo che viene utilizzato per decodificare le intestazioni MIME. Questo metodo è anche interno, ma viene chiamato abbastanza direttamente in un paio di punti, ad esempio, il setter Attachment.Name . Una dimostrazione:

 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); } } } 

Produce l’output:

¡Hola, _señor!

Potrebbe essere necessario eseguire alcuni test per garantire il ritorno del carrello, ecc. Sono trattati correttamente anche se in un test rapido ho fatto sembrano essere. Tuttavia, potrebbe non essere saggio fare affidamento su questa funzionalità a meno che il tuo caso d’uso sia abbastanza vicino alla decodifica di una stringa di intestazione MIME che non pensi che sarà interrotta da eventuali modifiche apportate alla libreria. Forse starai meglio scrivendo il tuo decodificatore stampabile.

Ho esteso la soluzione di Martin Murphy e spero che funzionerà in ogni caso.

 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; } 

L’ho scritto molto velocemente.

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

Se si sta decodificando la quotatura-stampabile con la codifica UTF-8, è necessario essere consapevoli del fatto che non è ansible decodificare ogni sequenza stampabile quotata una alla volta come gli altri hanno mostrato se ci sono serie di caratteri stampabili quotati insieme.

Ad esempio, se hai la seguente sequenza = E2 = 80 = 99 e decodifica questo usando UTF8 uno alla volta ottieni tre caratteri “strani” – se invece costruisci un array di tre byte e converti i tre byte con la codifica UTF8 si ottiene un unico aphostrope.

Ovviamente, se si utilizza la codifica ASCII, non è un problema una volta per volta, tuttavia la decodifica viene eseguita significa che il codice funzionerà indipendentemente dal codificatore di testo utilizzato.

Oh e non dimenticare = 3D è un caso speciale che significa che devi decodificare qualunque cosa tu abbia ancora una volta … Questo è un pazzesco trucco!

Spero possa aiutare

Questo decodificatore stampabile quotato funziona alla grande!

 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; } 

L’unico che ha funzionato per me.

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

Se hai solo bisogno di decodificare i QP, tira all’interno del tuo codice quelle tre funzioni dal link qui sopra:

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

E poi solo:

 var humanReadable = Decode(myQPString); 

Godere

Stavo cercando una soluzione dynamic e ho trascorso 2 giorni a provare soluzioni diverse. Questa soluzione supporterà i caratteri giapponesi e altri set di caratteri standard

 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()); } } 

Quindi puoi chiamare la funzione con

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

Questo è stato originariamente trovato qui

Soluzione migliore

  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); } 

A volte la stringa in un file EML è composta da diverse parti codificate. Questa è una funzione per utilizzare il metodo di Dave per questi casi:

 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; } 

Nota: le soluzioni con “input.Replace” sono tutte su Internet e tuttavia non sono corrette.

Vedi, se hai UN simbolo decodificato e poi usi “sostituisci” , TUTTI i simboli in “input” saranno sostituiti, e quindi tutta la seguente decodifica verrà interrotta.

Soluzione più corretta:

 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; }