关于.net:如何告诉ASCIIEncoding类不要解码字节顺序标记

How to tell ASCIIEncoding class not to decode the byte order mark

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

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

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

一些字符串将如下所示:

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

然后当我称之为:

1
XElement.Parse(someString)

前三个字节引发异常:ef bb bf-utf8字节顺序标记。所以我认为,如果我指定utf8编码,而不是默认编码,比如:

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

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


请不要使用

1
ASCIIEncoding.UTF8

那真的只是

1
Encoding.UTF8

它根本不使用ASCIIEncoding。它在源代码中看起来很像。

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

如果你用File.ReadAllText读取文件,我怀疑它会自动删除BOM。或者你可以在打电话给XElement.Parse之前,在之后再修剪一下。使用错误的编码(ascii或encoding.default)不是正确的方法。同样,它也不是垃圾人物。它是一个非常有用的字符,非常明显地表明它确实是一个utf-8文件,只是在这个特定的上下文中不需要它。"垃圾"给人的印象是它是损坏的数据,不应该出现在文件中,而事实肯定不是这样。

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

1
2
3
4
5
XElement element;
using (XmlReader reader = XmlReader.Create(new MemoryStream(bytes))
{
    element = XElement.Load(reader);
}

这样就可以自动检测编码。


System.Text.Encoding.GetString()保留存在的BOM,并将其转换为UTF-16 BOM(U+FEFF)。把这当作一个特性。严格地说,这是正确的做法,因为抛出BOM会使转换丢失,而不会使转换循环。不过,有点令人惊讶的是,它们没有提供一个标志来让您指定所需的行为,但您确实做到了。所以…你有两个选择:

  • 转换为字符串,在对该字符串调用XElement.Parse()之前查找并删除该BOM。或者…

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

  • 你的选择。下面是一些适用的示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    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 ;
            }
        }
    }


    这不是一个答案,但是评论中的代码是可怕的,把它放在你的问题中感觉有点粗鲁。你真的想这么做吗?

    1
    2
    3
    4
    5
    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"))));

    想知道你为什么得到:

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

    我当然是…