C#: Classe pour le décodage Cité-encodage imprimable?

y a-t-il une classe existante dans C# qui peut convertir Quoted-Printable encodage String? Cliquez sur le lien ci-dessus pour obtenir plus d'informations sur le codage.

ce qui suit est cité du lien ci-dessus pour votre commodité.

toute valeur de octet 8 bits peut être encodée avec 3 caractères, un " = " suivi de deux chiffres hexadécimaux (0-9 ou A-F) représentant la valeur numérique de l'octet. Par exemple, un aliment US-ASCII forme caractère (valeur décimale 12) peut être représenté par "=0C" , et un US-ASCII le signe égal (valeur décimale 61) représenté par "=3D". Tous les personnages sauf caractères ASCII imprimables ou les caractères de fin de ligne doivent être encodés dans ce mode.

tous les caractères ASCII imprimables (valeurs décimales comprises entre 33 et 126) peuvent être représentés par eux-mêmes, à l'exception de "=" (décimal 61).

onglet ASCII et caractères d'espace, les valeurs décimales 9 et 32, peuvent être représenté par eux-mêmes, sauf si ces caractères apparaissent à la fin de ligne. Si l'un de ces personnages apparaît à la fin d'une ligne, il doit être encodé comme "= 09" (tab) ou " = 20" (espace.)

Si les données codées contient les sauts de ligne significatifs, ils doivent être codé comme une séquence ASCII CR LF, pas comme leurs valeurs initiales. Inversement si les valeurs de byte 13 et 10 avoir une signification autre que fin de ligne alors ils doivent être encodés comme =0D et =0A.

lignes de données codées citées-imprimables ne doit pas dépasser 76 caractères. Pour satisfaire à cette exigence, sans modification du texte encodé, ligne douce des pauses peuvent être ajoutées au besoin. Doux le saut de ligne consiste en un "=" à la fin d'une codé en ligne, et ne pas provoquer un saut de ligne dans le décodé texte.

24
demandé sur Lopper 2010-02-09 06:27:52

12 réponses

il y a des fonctionnalités dans les bibliothèques framework pour faire cela, mais cela ne semble pas être exposé clairement. La mise en œuvre est dans la classe interne System.Net.Mime.QuotedPrintableStream. Cette classe définit une méthode appelée DecodeBytes qui fait ce que vous voulez. La méthode semble être utilisée par une seule méthode qui est utilisée pour décoder les en-têtes MIME. Cette méthode est également interne, mais est appelée assez directement à quelques endroits, par exemple, le Attachment.Name setter. Une démonstration:

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

Produit de la sortie:

¡Hola, _señor!

vous devrez peut-être faire quelques tests pour vous assurer que les retours de chariot, etc sont traités correctement, bien que dans un test rapide je l'ai fait semble-t-il. Cependant, il peut ne pas être sage de se fier à cette fonctionnalité à moins que votre dossier d'utilisation ne soit assez proche du décodage d'une chaîne D'en-tête MIME que vous ne pensez pas qu'elle sera brisée par des changements apportés à la bibliothèque. Vous feriez peut-être mieux d'écrire votre propre décodeur.

19
répondu Dave 2010-02-10 10:55:05

j'ai prolongé la solution de Martin Murphy et j'espère qu'elle fonctionnera dans tous les cas.

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;
}
16
répondu Igor Semkiv 2018-10-01 10:01:20

j'ai écrit ça vite fait.

    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", "");
    }       
6
répondu Martin Murphy 2016-09-08 20:29:01

si vous décodez quoted-printable avec l'encodage UTF-8, vous devez être conscient que vous ne pouvez pas décoder chaque séquence un-à-la-fois comme les autres l'ont montré s'il y a des passages de caractères imprimables cotés ensemble.

Par exemple, si vous avez la séquence suivante =E2=80=99 et décoder cette aide UTF8 un-à-un-temps vous obtenez trois "bizarre" personnages - si vous au lieu de construire un tableau de trois octets et de convertir les trois octets avec l'encodage UTF8 vous trouvez un seul aphostrope.

évidemment, si vous utilisez l'encodage ASCII, le one-at-a-time n'est pas un problème, mais le décodage signifie que votre code fonctionnera quel que soit l'encodeur de texte utilisé.

Oh and don't forget =3D est un cas spécial qui signifie que vous devez décoder ce que vous avez une fois de plus... C'est un gotcha fou!

Hope qui aide

4
répondu Demented Devil 2011-12-22 09:39:12

ce décodeur imprimable Cité fonctionne très bien!

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();
    }
2
répondu Gonzalo Gallotti 2012-02-15 14:21:45
    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;
    }
2
répondu iaceian 2016-04-22 21:55:28
private static string Decode(string input, string bodycharset) {
        var i = 0;
        var output = new List<byte>();
        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());
        }

    }

Ensuite, vous pouvez appeler la fonction avec

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

C'était à l'origine trouvé ici

2
répondu Lee Harris 2016-07-27 19:47:28

La seule qui a fonctionné pour moi.

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

si vous avez juste besoin de décoder le QPs, tirez à l'intérieur de votre code ces trois fonctions à partir du lien ci-dessus:

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

Et puis juste:

var humanReadable = Decode(myQPString);

Profitez de

1
répondu Pizzaboy 2013-09-17 11:55:55

meilleure solution

    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;
}
1
répondu Igor Semkiv 2017-08-01 03:41:40
public static string DecodeQuotedPrintables(string input, Encoding encoding)
    {
        var regex = new Regex(@"\=(?<Symbol>[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);
    }
0
répondu Kachalov Sergey 2014-04-09 06:44:24

parfois, la chaîne dans un fichier EML est composée par plusieurs parties codées. C'est une fonction pour utiliser le Dave méthode pour ces cas:

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;
}
0
répondu Armando Saenz 2016-12-21 15:09:36

s'il vous Plaît note: avec les solutions d'entrée".Remplacer" sont partout sur Internet et encore ils ne sont pas corrects.

voir, si vous avez un symbole décodé et ensuite utilisez "replace", les symboles de "input" seront remplacés, et tous les décodages suivants seront brisés.

solution plus correcte:

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;
    }
0
répondu Don Joe 2018-02-02 20:15:31