如何转换引用打印字符串

Posted

技术标签:

【中文标题】如何转换引用打印字符串【英文标题】:How to convert Quoted-Print String 【发布时间】:2016-09-29 03:27:32 【问题描述】:

我正在 .NET 中处理法语字符串 解码邮件正文,我收到“Chasn=C3=A9 sur illet” 我想得到“Chasné sur illet” 而且我在 2 天的网络搜索中找不到任何解决方案。

C# 或 VB.NET 谁能帮帮我?

谢谢

【问题讨论】:

你能在你设置字符串的地方发布你的代码吗? 您好,我的字符串来自 IMAP 服务器 我阅读了消息并使用 IMAP 命令获取消息正文文本:FETCH BODY[TEXT] 它返回给我一个以 Quoted_printable 格式编码的字符串,但我没有'找不到任何想法做转换器 @MarcCollin 请参阅下面的完整代码。 【参考方案1】:

这是 UTF8 编码。

使用这篇文章:

http://www.dpit.co.uk/decoding-quoted-printable-email-in-c/

这是代码(如果有帮助别忘了接受答案):

using System;
using System.Text;
using System.Text.RegularExpressions;

namespace ConsoleApplication1

    class Program
    
        static void Main(string[] args)
        

            Console.WriteLine(DecodeQuotedPrintable("Chasn=C3=A9 sur illet"));
            Console.ReadKey();
        

        static string DecodeQuotedPrintable(string input)
        
            var occurences = new Regex(@"(=[0-9A-Z][0-9A-Z])+", RegexOptions.Multiline);
            var matches = occurences.Matches(input);
            foreach (Match m in matches)
            
                byte[] bytes = new byte[m.Value.Length / 3];
                for (int i = 0; i < bytes.Length; i++)
                
                    string hex = m.Value.Substring(i * 3 + 1, 2);
                    int iHex = Convert.ToInt32(hex, 16);
                    bytes[i] = Convert.ToByte(iHex);
                
                input = input.Replace(m.Value, Encoding.UTF8.GetString(bytes));
            
            return input.Replace("=rn", "");
        
    

【讨论】:

【参考方案2】:
    static string ConverFromHex(string source)
    
        string target = string.Empty;

        int startPos = source.IndexOf('=', 0);
        int prevStartPos = 0;
        while (startPos >= 0)
        
            // concat with substring from source
            target += source.Substring(prevStartPos, startPos - prevStartPos);

            // next offset
            startPos++;

            // update prev pos
            prevStartPos = startPos;

            // get substring
            string hexString = source.Substring(startPos, 2);

            // get int equiv
            int hexNum = 0;
            if (int.TryParse(hexString, System.Globalization.NumberStyles.AllowHexSpecifier, System.Globalization.CultureInfo.InvariantCulture, out hexNum))
            
                // add to target string
                target += (char)hexNum;

                // add hex length
                prevStartPos += 2;
            

            // next occurence
            startPos = source.IndexOf('=', startPos);
        

        // add rest of source
        target += source.Substring(prevStartPos);

        return target;
    

【讨论】:

这段代码没有正确处理 OP 的字符串,结果是“Chasné sur illet”。您的代码将每个十六进制数字视为一个单独的字符,但“=C3=A9”应该代表单个字符“é”。【参考方案3】:

发件人:https://***.com/a/36803911/6403521 我的解决方案:

    [TestMethod]
    public void TestMethod1()
    

        Assert.AreEqual("La Bouichère", quotedprintable("La Bouich=C3=A8re", "utf-8"));
        Assert.AreEqual("Chasné sur illet", quotedprintable("Chasn=C3=A9 sur illet", "utf-8"));
        Assert.AreEqual("é è", quotedprintable("=C3=A9 =C3=A8", "utf-8"));
    
    private string quotedprintable(string pStrIn, string encoding)
    
        String strOut = pStrIn.Replace("=\r\n", "");
        // Find the first =
        int position = strOut.IndexOf("=");
        while (position != -1)
         
            // String before the =
            string leftpart = strOut.Substring(0, position);
            // get the QuotedPrintable String in a ArrayList
            System.Collections.ArrayList hex = new System.Collections.ArrayList();
            // The first Part
            hex.Add(strOut.Substring(1 + position, 2));
            // Look for the next parts
            while (position + 3 < strOut.Length && strOut.Substring(position + 3, 1) == "=")
            
                position = position + 3;
                hex.Add(strOut.Substring(1 + position, 2));
            
            // In the hex Array, we have two items 
            // Convert using the GetEncoding Function
            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);
            // Part of the orignal String after the last QP Symbol
            string rightpart = strOut.Substring(position + 3);
            // Re build the String
            strOut = leftpart + equivalent + rightpart;
            // find the new QP Position
            position = leftpart.Length + equivalent.Length;
            if (rightpart.Length == 0)
            
                position = -1;
            
            else
            
                position = strOut.IndexOf("=", position + 1);
            
        
        return strOut;
    

【讨论】:

天哪,这太复杂了。你试过我的答案了吗?【参考方案4】:

或者最简单的,只需使用我的MimeKit 库中的QuotedPrintableDecoder:

static string DecodeQuotedPrintable (string input, string charset)

    var decoder = new QuotedPrintableDecoder ();
    var buffer = Encoding.ASCII.GetBytes (input);
    var output = new byte[decoder.EstimateOutputLength (buffer.Length)];
    int used = decoder.Decode (buffer, 0, buffer.Length, output);
    var encoding = Encoding.GetEncoding (charset);
    return encoding.GetString (output, 0, used);

请注意,上面的其他答案假设解码的内容是 ASCII 或 UTF-8,但不一定是这种情况。您需要从您正在解码的 MIME 部分的 Content-Type 标头中获取 charset 参数。

当然...如果您不知道如何获取该信息,您可以简单地使用我很棒的 MailKit 库从 IMAP 获取 MIME 部分并让它为您完成所有这些工作。

【讨论】:

MimeKit 看起来不错,但它希望能够加载流对象。我明白为什么总体上会更好,但是如果您现有的代码库无法在文件加载步骤中轻松放入 MimeKit,AFAICT 就没有将它与字符串一起使用的功能。 FWIW,并非所有电子邮件都可以正确转换为 C# 字符串。问题是电子邮件可以有多个文本部分,每个文本部分使用不同的字符集编码,这意味着通过将其转换为字符串,它将被损坏。只是让您长期了解的事情。【参考方案5】:

我们在使用这种方法时遇到了问题 - 它非常慢。 以下增强性能A LOT

public static string FromMailTransferEncoding(this string messageText, Encoding enc, string transferEncoding)

    if (string.IsNullOrEmpty(transferEncoding)) 
        return messageText;

    if ("quoted-printable".Equals(transferEncoding.ToLower())) 
    
        StringBuilder sb = new StringBuilder();               
        string delimitorRegEx = @"=[\r][\n]";
        string[] parts = Regex.Split(messageText, delimitorRegEx);

        foreach (string part in parts)
        
            string subPart = part;
            Regex occurences = new Regex(@"(=[0-9A-Z][0-9A-Z])+", RegexOptions.Multiline);
            MatchCollection matches = occurences.Matches(subPart);

            foreach (Match m in matches)
            
                byte[] bytes = new byte[m.Value.Length / 3];
                for (int i = 0; i < bytes.Length; i++)
                
                    string hex = m.Value.Substring(i * 3 + 1, 2);
                    int iHex = Convert.ToInt32(hex, 16);
                    bytes[i] = Convert.ToByte(iHex);
                

                subPart = occurences.Replace(subPart, enc.GetString(bytes), 1);
            

            sb.Append(subPart);
        
        return sb.ToString();
            
return messageText;

【讨论】:

以上是关于如何转换引用打印字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何以类似 JSON 的格式打印圆形结构?

如何将 HTML 转换为字符串 android 并打印该字符串

如何将二进制整数转换为十六进制字符串?

如何打印2个int但没有添加?

如何将十六进制字符串转换为十六进制数字

如何使用 stoi() 将字符串转换为整数?