【问题标题】:Convert String (UTF-16) to UTF-8 in C#在 C# 中将字符串 (UTF-16) 转换为 UTF-8
【发布时间】:2011-09-06 03:00:02
【问题描述】:

我需要在 C# 中将字符串转换为 UTF-8。我已经尝试了很多方法,但没有一个能如我所愿。 我将字符串转换为字节数组,然后尝试将其写入 XML 文件(编码为 UTF-8 ....),但要么我得到相同的字符串(根本没有编码)要么我得到一个列表字节是无用的.... 有人遇到同样的问题吗?

编辑: 这是我使用的一些代码:

str= "testé";
byte[] utf8Bytes = Encoding.UTF8.GetBytes(str);
return Encoding.UTF8.GetString(utf8Bytes);

结果是“testé”,或者我期望像“testé”这样的东西......

【问题讨论】:

  • 您现有的代码会更好地解释您的问题,如果不是字节列表或可读字符串,您希望得到什么?当然,在 XML 中,一个可读的字符串正是你想要的?
  • 另外,当您说“完全没有编码相同的字符串”时,您是什么意思?如果您获取一个 UTF-16 字符串,并将其保存为 UTF-8 编码的 XML 文件,然后在文本编辑器中打开该 XML 文件,您将看到“相同的字符串”。如果您使用十六进制编辑器打开文件,您只会注意到差异。

标签: c# .net encoding utf-8


【解决方案1】:

如果你想要一个 UTF8 字符串,其中每个字节都是正确的('Ö' -> [195, 0] , [150, 0]),你可以使用如下:

public static string Utf16ToUtf8(string utf16String)
{
   /**************************************************************
    * Every .NET string will store text with the UTF16 encoding, *
    * known as Encoding.Unicode. Other encodings may exist as    *
    * Byte-Array or incorrectly stored with the UTF16 encoding.  *
    *                                                            *
    * UTF8 = 1 bytes per char                                    *
    *    ["100" for the ansi 'd']                                *
    *    ["206" and "186" for the russian 'κ']                   *
    *                                                            *
    * UTF16 = 2 bytes per char                                   *
    *    ["100, 0" for the ansi 'd']                             *
    *    ["186, 3" for the russian 'κ']                          *
    *                                                            *
    * UTF8 inside UTF16                                          *
    *    ["100, 0" for the ansi 'd']                             *
    *    ["206, 0" and "186, 0" for the russian 'κ']             *
    *                                                            *
    * We can use the convert encoding function to convert an     *
    * UTF16 Byte-Array to an UTF8 Byte-Array. When we use UTF8   *
    * encoding to string method now, we will get a UTF16 string. *
    *                                                            *
    * So we imitate UTF16 by filling the second byte of a char   *
    * with a 0 byte (binary 0) while creating the string.        *
    **************************************************************/

    // Storage for the UTF8 string
    string utf8String = String.Empty;

    // Get UTF16 bytes and convert UTF16 bytes to UTF8 bytes
    byte[] utf16Bytes = Encoding.Unicode.GetBytes(utf16String);
    byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes);

    // Fill UTF8 bytes inside UTF8 string
    for (int i = 0; i < utf8Bytes.Length; i++)
    {
        // Because char always saves 2 bytes, fill char with 0
        byte[] utf8Container = new byte[2] { utf8Bytes[i], 0 };
        utf8String += BitConverter.ToChar(utf8Container, 0);
    }

    // Return UTF8
    return utf8String;
}

在我的情况下,DLL 请求也是一个 UTF8 字符串,但不幸的是,UTF8 字符串必须使用 UTF16 编码('Ö' -> [195, 0], [19, 32])进行解释。因此 ANSI '-' 即 150 必须转换为 UTF16 '-' 即 8211。如果您也有这种情况,您可以使用以下代码:

public static string Utf16ToUtf8(string utf16String)
{
    // Get UTF16 bytes and convert UTF16 bytes to UTF8 bytes
    byte[] utf16Bytes = Encoding.Unicode.GetBytes(utf16String);
    byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes);

    // Return UTF8 bytes as ANSI string
    return Encoding.Default.GetString(utf8Bytes);
}

或者原生方法:

[DllImport("kernel32.dll")]
private static extern Int32 WideCharToMultiByte(UInt32 CodePage, UInt32 dwFlags, [MarshalAs(UnmanagedType.LPWStr)] String lpWideCharStr, Int32 cchWideChar, [Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder lpMultiByteStr, Int32 cbMultiByte, IntPtr lpDefaultChar, IntPtr lpUsedDefaultChar);

public static string Utf16ToUtf8(string utf16String)
{
    Int32 iNewDataLen = WideCharToMultiByte(Convert.ToUInt32(Encoding.UTF8.CodePage), 0, utf16String, utf16String.Length, null, 0, IntPtr.Zero, IntPtr.Zero);
    if (iNewDataLen > 1)
    {
        StringBuilder utf8String = new StringBuilder(iNewDataLen);
        WideCharToMultiByte(Convert.ToUInt32(Encoding.UTF8.CodePage), 0, utf16String, -1, utf8String, utf8String.Capacity, IntPtr.Zero, IntPtr.Zero);

        return utf8String.ToString();
    }
    else
    {
        return String.Empty;
    }
}

如果您需要它,请参阅Utf8ToUtf16。 希望我能有所帮助。

【讨论】:

  • 没有理由打电话给Encoding.Unicode.GetBytes()然后Encoding.Convert,直接打电话给Encoding.UTF8.GetBytes()
  • 前两种方法甚至对我不起作用,正如@Thomas Levesque 所说,“C# 中的字符串始终是 UTF-16,没有办法“转换”它”。因此,由于我正在调用一个需要宽字符串的本机函数,并且我需要取回一个解码的字符串,因此您的本机方法对我有用。
  • @leetNightshade 我很抱歉读到这个,它在调用 Delphi-DLL 的方法时对我有用,所以我把它们贴在这里。我希望原生方式对你有用,无论如何我都可以帮助你。
  • @MEN。非常感谢。一个问题,我测试的每个字符串都使用第二种方法(Encoding.Default)。当用户的操作系统具有不同的默认值时,这会成为问题吗?
  • @Lara 迟到但并非永远不会。是的,它应该可以工作,因为您可以在“docs.microsoft.com/en-us/dotnet/api/system.text.encoding”的文档中找到“对于 ANSI 编码,最适合的行为是默认行为。”
【解决方案2】:

C# 中的字符串总是 UTF-16,没有办法“转换”它。只要您在内存中操作字符串,编码就无关紧要,仅当您将字符串写入流(文件,内存流,网络流......)时才重要。

如果要将字符串写入 XML 文件,只需在创建 XmlWriter 时指定编码

【讨论】:

  • 我终于找到了解决方案...我已经尝试了您建议的所有方法,但对我不起作用...这段代码对我有用:使用(TextWriter writer = new StreamWriter(filename) { xmlDoc.Save(writer); }
  • @Celero,这不完全是我的建议,但它是等价的...... StreamWriter 默认使用 UTF-8
【解决方案3】:
    private static string Utf16ToUtf8(string utf16String)
    {
        /**************************************************************
         * Every .NET string will store text with the UTF16 encoding, *
         * known as Encoding.Unicode. Other encodings may exist as    *
         * Byte-Array or incorrectly stored with the UTF16 encoding.  *
         *                                                            *
         * UTF8 = 1 bytes per char                                    *
         *    ["100" for the ansi 'd']                                *
         *    ["206" and "186" for the russian '?']                   *
         *                                                            *
         * UTF16 = 2 bytes per char                                   *
         *    ["100, 0" for the ansi 'd']                             *
         *    ["186, 3" for the russian '?']                          *
         *                                                            *
         * UTF8 inside UTF16                                          *
         *    ["100, 0" for the ansi 'd']                             *
         *    ["206, 0" and "186, 0" for the russian '?']             *
         *                                                            *
         * We can use the convert encoding function to convert an     *
         * UTF16 Byte-Array to an UTF8 Byte-Array. When we use UTF8   *
         * encoding to string method now, we will get a UTF16 string. *
         *                                                            *
         * So we imitate UTF16 by filling the second byte of a char   *
         * with a 0 byte (binary 0) while creating the string.        *
         **************************************************************/

        // Get UTF16 bytes and convert UTF16 bytes to UTF8 bytes
        byte[] utf16Bytes = Encoding.Unicode.GetBytes(utf16String);
        byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes);
        char[] chars = (char[])Array.CreateInstance(typeof(char), utf8Bytes.Length);

        for (int i = 0; i < utf8Bytes.Length; i++)
        {
            chars[i] = BitConverter.ToChar(new byte[2] { utf8Bytes[i], 0 }, 0);
        }

        // Return UTF8
        return new String(chars);
    }

在原帖作者连接字符串。每个 sting 操作都会在 .Net 中重新创建字符串。 String 实际上是一种引用类型。结果,提供的功能将明显变慢。不要那样做。改用字符数组,直接写在那里,然后将结果转换为字符串。在我处理 500 kb 文本的情况下,差异几乎是 5 分钟。

【讨论】:

  • 其实还是有错误。如果 utf16Bytes 数组实际上包含 2 字节符号,则转换将无法正常工作,因为我们只是丢弃了高字节
【解决方案4】:

查看 Jon Skeet 对另一个问题的回答:UTF-16 to UTF-8 conversion (for scripting in Windows)

它包含您需要的源代码。

希望对你有帮助。

【讨论】:

    【解决方案5】:

    这个例子有帮助吗?

    using System;
    using System.IO;
    using System.Text;
    
    class Test
    {
       public static void Main() 
       {        
        using (StreamWriter output = new StreamWriter("practice.txt")) 
        {
            // Create and write a string containing the symbol for Pi.
            string srcString = "Area = \u03A0r^2";
    
            // Convert the UTF-16 encoded source string to UTF-8 and ASCII.
            byte[] utf8String = Encoding.UTF8.GetBytes(srcString);
            byte[] asciiString = Encoding.ASCII.GetBytes(srcString);
    
            // Write the UTF-8 and ASCII encoded byte arrays. 
            output.WriteLine("UTF-8  Bytes: {0}", BitConverter.ToString(utf8String));
            output.WriteLine("ASCII  Bytes: {0}", BitConverter.ToString(asciiString));
    
    
            // Convert UTF-8 and ASCII encoded bytes back to UTF-16 encoded  
            // string and write.
            output.WriteLine("UTF-8  Text : {0}", Encoding.UTF8.GetString(utf8String));
            output.WriteLine("ASCII  Text : {0}", Encoding.ASCII.GetString(asciiString));
    
            Console.WriteLine(Encoding.UTF8.GetString(utf8String));
            Console.WriteLine(Encoding.ASCII.GetString(asciiString));
        }
    }
    

    }

    【讨论】:

      【解决方案6】:
      class Program
      {
          static void Main(string[] args)
          {
              String unicodeString =
              "This Unicode string contains two characters " +
              "with codes outside the traditional ASCII code range, " +
              "Pi (\u03a0) and Sigma (\u03a3).";
      
              Console.WriteLine("Original string:");
              Console.WriteLine(unicodeString);
              UnicodeEncoding unicodeEncoding = new UnicodeEncoding();
              byte[] utf16Bytes = unicodeEncoding.GetBytes(unicodeString);
              char[] chars = unicodeEncoding.GetChars(utf16Bytes, 2, utf16Bytes.Length - 2);
              string s = new string(chars);
              Console.WriteLine();
              Console.WriteLine("Char Array:");
              foreach (char c in chars) Console.Write(c);
              Console.WriteLine();
              Console.WriteLine();
              Console.WriteLine("String from Char Array:");
              Console.WriteLine(s);
      
              Console.ReadKey();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2014-02-05
        • 2014-02-22
        • 1970-01-01
        • 2012-02-22
        • 1970-01-01
        • 2010-09-21
        • 1970-01-01
        • 2015-09-21
        相关资源
        最近更新 更多