C #: Класс для декодирования Quoted-Printable кодирования? - PullRequest
26 голосов
/ 09 февраля 2010

Существует ли в C # класс, который может преобразовать кодировку Quoted-Printable в String? Нажмите на ссылку выше, чтобы получить больше информации о кодировке.

Следующее цитируется по вышеуказанной ссылке для вашего удобства.

Может быть закодировано любое 8-битное значение байта с 3 символами, "=", сопровождаемый две шестнадцатеричные цифры (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 символов. Чтобы удовлетворить это требование без изменение закодированного текста, мягкая линия Перерывы могут быть добавлены по желанию. Мягкий разрыв строки состоит из "=" в конец закодированной строки, и не вызвать разрыв строки в декодированном текст.

Ответы [ 12 ]

19 голосов
/ 10 февраля 2010

В библиотеках фреймворка есть функциональные возможности для этого, но, похоже, они не выставлены полностью. Реализация находится во внутреннем классе System.Net.Mime.QuotedPrintableStream. Этот класс определяет метод с именем DecodeBytes, который делает то, что вы хотите. По-видимому, этот метод используется только одним методом, который используется для декодирования заголовков MIME. Этот метод также является внутренним, но вызывается довольно напрямую в нескольких местах, например, в установщике Attachment.Name. Демонстрация:

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!

Возможно, вам придется провести некоторое тестирование, чтобы убедиться, что возврат каретки и т. Д. Обрабатываются правильно, хотя в ходе быстрого теста, который я сделал, они, похоже, были. Однако, возможно, не стоит полагаться на эту функциональность, если ваш вариант использования не достаточно близок к декодированию строки заголовка MIME, и вы не думаете, что она будет нарушена при любых изменениях в библиотеке. Возможно, вам лучше написать собственный декодер для печати на основе котировок.

17 голосов
/ 29 ноября 2011

Я расширил решение Мартина Мерфи, и я надеюсь, что оно будет работать в каждом случае.

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;
}
7 голосов
/ 11 мая 2011

Я написал это очень быстро.

    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<string>(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", "");
    }       
5 голосов
/ 22 декабря 2011

Если вы декодируете печатаемую в кавычки с помощью кодировки UTF-8, вам нужно знать, что вы не можете декодировать каждую печатаемую в кавычки последовательность по одному, как другие показали, если есть серии печатных символов в кавычках вместе.

Например - если у вас есть следующая последовательность = E2 = 80 = 99 и декодируете ее, используя UTF8 по одному, вы получите три «странных» символа - если вместо этого вы создадите массив из трех байтов и преобразуете три байта с кодировкой UTF8 вы получаете один апостроп.

Очевидно, что если вы используете кодировку ASCII, то поочередно проблем не возникает, однако выполнение декодирования означает, что ваш код будет работать независимо от используемого кодировщика текста.

Да, и не забывайте = 3D - это особый случай, который означает, что вам нужно декодировать все, что у вас есть еще раз ... Это сумасшедшая ошибка!

Надеюсь, что поможет

2 голосов
/ 27 июля 2016

Я искал динамическое решение и потратил 2 дня, пробуя разные решения. Это решение будет поддерживать японские символы и другие стандартные наборы символов

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

    }

Затем вы можете вызвать функцию с помощью

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

Это было первоначально найдено здесь

2 голосов
/ 23 апреля 2016
    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 голосов
/ 15 февраля 2012

Этот цитируемый печатный декодер прекрасно работает!

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();
    }
1 голос
/ 17 сентября 2013

Единственный, который работал на меня.

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

Если вам просто нужно декодировать QP, извлеките из своего кода эти три функции из ссылки выше:

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

А потом просто:

var humanReadable = Decode(myQPString);

Наслаждайтесь

1 голос
/ 01 февраля 2012

Лучшее решение

    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;
}
0 голосов
/ 02 февраля 2018

Обратите внимание: Решения с «input.Replace» распространены по всему Интернету, и все же они не верны.

Смотрите, если у вас есть ОДИН декодированный символ, а затем используйте "replace" , ALL символы во «входе» будут заменены, а затем все последующее декодирование будет прервано.

Более правильное решение:

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