【问题标题】:How to tell ASCIIEncoding class not to decode the byte order mark如何告诉 ASCIIEncoding 类不要解码字节顺序标记
【发布时间】:2011-07-03 04:41:09
【问题描述】:

当使用 .net ASCIIEncoding 类将字节数组解码为字符串时,我是否需要编写一些代码来检测和删除字节顺序标记,或者是否可以告诉 ASCIIEncoding 不将字节顺序标记解码为字符串?

这是我的问题,当我这样做时:

string someString = System.Text.ASCIIEncoding.Default.GetString(someByteArray)

someString 将如下所示:

<?xml version="1.0"?>.......

然后当我这样称呼时:

XElement.Parse(someString)

由于前三个字节而引发异常:EF BB BF - UTF8 字节顺序标记。所以我想如果我指定UTF8编码,而不是Default,像这样:

System.Text.ASCIIEncoding.UTF8.GetString(someByteArray)

ASCIIEncoding 不会尝试将字节顺序标记解码为字符串。当我将返回的字符串复制到 notepad++ 中时,我可以看到一个 ? XML 标记前面的字符。所以现在字节顺序标记被解码为一个垃圾字符。在这种情况下,阻止字节顺序标记被解码的最佳方法是什么?

【问题讨论】:

  • someByteArray 中是否有这些 EF BB BF 字符?它们不是 ASCII,以 7F 结尾。
  • 我找到了一个很好的解释,包括一个可以选择正确编码并根据需要去除字节顺序标记的类,在这个站点:mobzystems.com/code/detecting-text-encoding.aspx

标签: .net utf-8 ascii byte-order-mark


【解决方案1】:

System.Text.Encoding.GetString() 保留 BOM(如果存在)并将其转换为 UTF-16 BOM (U+FEFF)。认为这是一个功能。严格来说,这是正确的做法,因为扔 BOM 会使转换有损且不可往返。不过,有点令人惊讶的是,他们没有提供一个标志来让你指定所需的行为,但你就在那里。所以...你有两个选择:

  1. 转换为字符串,查找 BOM 并将其删除,然后再对字符串调用 XElement.Parse()。或者……

  2. byte[] 包装在MemoryStream 中,将MemoryStream 包装在StreamReader 中,并使用XElement.Load() 进行解析。

您的选择。以下是一些可行的示例代码:

using System.IO;
using System.Text;
using System.Xml.Linq;

namespace TestDrive
{
    class Program
    {
        public static void Main()
        {
            byte[] octets = File.ReadAllBytes( "utf8-encoded-document-with-BOM.xml" ) ;

            // -----------------------------------------------
            // option 1: use a memory stream and stream reader
            // -----------------------------------------------
            using ( MemoryStream ms = new MemoryStream( octets) )
            using ( StreamReader sr = new StreamReader( ms , Encoding.UTF8 , true )   )
            {
                XElement element1 = XElement.Load( sr ) ;
            }

            // --------------------------------------------------------------------
            // option 2: convert to string, then look for and remove BOM if present
            // 
            // The .Net framework Encoding.GetString() methods preserve the BOM if
            // it is present. Since the internal format of .Net string is UTF-16,
            // the BOM is converted to the UTF-16 encoding (U+FEFF).
            // 
            // Consider this a feature.
            // --------------------------------------------------------------------
            // convert to UTF-16 string
            string       xml       = Encoding.UTF8.GetString( octets ) ;
            // Two different ways of getting the BOM
            //string UTF16_BOM = Encoding.Unicode.GetString(Encoding.Unicode.GetPreamble()).ToCharArray() ;
            const string UTF16_BOM = "\uFEFF" ; 
            // parse the element, removing the BOM if we see it.
            XElement element2 = XElement.Parse( xml.StartsWith( UTF16_BOM ) ? xml.Substring(1) : xml ) ;

            return ;
        }
    }
}

【讨论】:

    【解决方案2】:

    这不是一个答案,但是 cmets 中的代码很糟糕,把这个放在你的问题中感觉有点粗鲁。你真的要这样做吗:

    Byte[] bytes = new byte [] { 0xEF,0xBB,0xBF, 0x57, 0x44 };
    String txt = Encoding.UTF8.GetString(bytes);
    Console.WriteLine("String length {0}", txt.Length);
    Console.WriteLine("String '{0}'", txt);
    Console.WriteLine("Chars '{0}'", String.Join(",", txt.Select(chr => ((int)chr).ToString("x2"))));
    

    想知道你为什么会得到:

    String length 3
    String 'WD'
    String 'feff,57,44'
    

    我当然是……

    【讨论】:

    • 是的,但我没想到会在 .NET 字符串中看到它——我希望它出现在编码器/解码器的 byte[] 端,而不是字符串端。跨度>
    • 是的,这正是我想知道的。我不希望 GetString() 返回有关文件编码的信息。
    • 因为剥离 BOM 会导致无法往返的有损转换。
    • @Nicholas:是的,你是对的。感谢您的评论,它确实帮助我理解了 Encoding 类的预期用途。
    【解决方案3】:

    请不要使用

    ASCIIEncoding.UTF8
    

    真的只是

    Encoding.UTF8
    

    它根本没有使用ASCIIEncoding。它只是看起来像在您的源代码中。

    从根本上说,问题在于您的文件 UTF-8,它不是 ASCII。这就是为什么它有一个 UTF-8 字节顺序标记。我强烈建议您使用 Encoding.UTF8 以一种或另一种方式读取 UTF-8 文件。

    如果您使用File.ReadAllText 读取文件,我怀疑它会自动删除 BOM。或者你可以在之后修剪它,然后再打电话给XElement.Parse。使用错误的编码(ASCII 或 Encoding.Default)不是正确的方法。同样,它 不是 一个垃圾字符。这是一个非常有用的字符,非常强烈地表明它确实一个 UTF-8 文件 - 只是你不希望它在这个特定的上下文中。 “垃圾”给人的印象是它是不应该出现在文件中的损坏数据,而且绝对不是这种情况。

    另一种方法是完全避免将其转换为文本。例如:

    XElement element;
    using (XmlReader reader = XmlReader.Create(new MemoryStream(bytes))
    {
        element = XElement.Load(reader);
    }
    

    这样编码将被自动检测。

    【讨论】:

    • 什么时候 - 投票否决 JS 没有正确阅读问题......虽然你对文体评论非常正确并且他不应该使用 .ASCII.GetString(),他接着说他尝试过 .UTF8.GetString 并留下了一个虚假的字符。 (它也是,我刚刚在 linqpad 中尝试过)
    • @Will:我明白了。但他仍然认为他正在使用 ASCIIEncoding,我怀疑,鉴于帖子的标题。我认为他此刻非常很困惑。我现在建议了两种替代方法,但他确实应该使用 UTF8,而他不应该通过ASCIIEncoding.UTF8 这样做,这是可恶的。跨度>
    • 乔恩 - 我会带走 -1 - 玩得很开心。但是这里有一些非常奇怪的东西(对我来说)——看看我的另一个“答案”......
    • 感谢 Jon,XmlReader 完全符合我的要求。关于“垃圾字符”不是垃圾,您也是对的,但它所代表的信息在字符串中似乎与上下文无关。
    猜你喜欢
    • 1970-01-01
    • 2014-12-24
    • 1970-01-01
    • 2014-07-08
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    相关资源
    最近更新 更多