从C#中的字符串中删除字节顺序标记

我读过类似的文章,但他们没有回答我的问题。

在C#中,我有一个从WebClient.DownloadString获得的字符串。 我尝试将client.Encoding设置为新的UTF8Encoding(false),但这没什么区别-我仍然在结果字符串的开头以UTF-8的字节顺序标记结束。 我需要删除它(以便使用LINQ解析生成的XML),并希望在内存中删除它。

所以我有一个以\ x00EF \ x00BB \ x00BF开头的字符串,如果存在,我想删除它。 现在我正在使用

if (xml.StartsWith(ByteOrderMarkUtf8))
{
    xml = xml.Remove(0, ByteOrderMarkUtf8.Length);
}

但这感觉不对。 我已经尝试了各种具有流,GetBytes和编码的代码,但是没有任何效果。 谁能提供从字符串中剥离BOM的“正确”算法?

谢谢!

TrueWill asked 2020-07-31T06:23:58Z
12个解决方案
46 votes

我最近在.net 4升级方面遇到问题,但在此之前,简单的答案是

String.Trim()

删除BOM表直到.net 3.5但是,在.net 4中,您需要对其稍作更改

String.Trim(new char[]{'\uFEFF'});

尽管您可能还希望删除“零宽度空间” U + 200B,但它也将摆脱“字节”顺序标记。

String.Trim(new char[]{'\uFEFF','\u200B'});

这也可以用来删除其他不需要的字符

来自一些进一步的信息[http://msdn.microsoft.com/en-us/library/t97s7bs3.aspx]

.NET Framework 3.5 SP1和更早版本维护此方法修剪的内部空白字符列表。 从.NET Framework 4开始,该方法将修剪所有Unicode空格字符(即,当将它们传递给Char.IsWhiteSpace方法时会产生真实返回值的字符)。 由于此更改,.NET Framework 3.5 SP1和更早版本中的Trim方法删除了两个字符,即ZERO WIDTH SPACE(U + 200B)和ZERO WIDTH NO-BREAK SPACE(U + FEFF),这是.NET中的Trim方法。 NET Framework 4和更高版本不会删除。 此外,.NET Framework 3.5 SP1和更早版本中的Trim方法不会修剪三个Unicode空格字符:MONGOLIAN VOWEL分隔符(U + 180E),NARROW NO-BREAK SPACE(U + 202F)和MEDIUM MATHEMATICAL SPACE (U + 205F)。

PJUK answered 2020-07-31T06:25:03Z
44 votes

我的测试数据不正确,这使我有些困惑。 基于如何在读取文件时避免跳入UTF-8 BOM的问题,我发现这可行:

private readonly string _byteOrderMarkUtf8 =
    Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());

public string GetXmlResponse(Uri resource)
{
    string xml;

    using (var client = new WebClient())
    {
        client.Encoding = Encoding.UTF8;
        xml = client.DownloadString(resource);
    }

    if (xml.StartsWith(_byteOrderMarkUtf8, StringComparison.Ordinal))
    {
        xml = xml.Remove(0, _byteOrderMarkUtf8.Length);
    }

    return xml;
}

正确设置客户端的Encoding属性可将BOM减少为单个字符。 但是,XDocument.Parse仍然不会读取该字符串。 这是我到目前为止提出的最干净的版本。

TrueWill answered 2020-07-31T06:25:28Z
30 votes

这也很好

int index = xmlResponse.IndexOf('<');
if (index > 0)
{
    xmlResponse = xmlResponse.Substring(index, xmlResponse.Length - index);
}
Vivek Ayer answered 2020-07-31T06:25:49Z
19 votes

如果变量xml是字符串类型,则您已经做错了-在字符串中,BOM不应表示为三个单独的字符,而应表示为单个代码点。 不要使用DownloadString,而要使用DownloadData并解析字节数组。 XML解析器应识别BOM本身,然后跳过它(自动检测为UTF-8编码的文档除外)。

Martin v. Löwis answered 2020-07-31T06:24:14Z
13 votes

直接将其从字符串中删除的一种快速简单的方法:

private static string RemoveBom(string p)
{
     string BOMMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
     if (p.StartsWith(BOMMarkUtf8))
         p = p.Remove(0, BOMMarkUtf8.Length);
     return p.Replace("\0", "");
}

如何使用:

string yourCleanString=RemoveBom(yourBOMString);
Tiago Gouvêa answered 2020-07-31T06:26:13Z
10 votes

我有一个非常相似的问题(我需要解析一个表示为字节数组的XML文档,该字节数组的开头带有字节顺序标记)。 我用马丁对他的回答的评论之一来解决。 我使用了我拥有的字节数组(而不是将其转换为字符串),并创建了一个xml对象。 然后,我将其传递给XDocument.Load,它就像一个护身符。 例如,假设xmlBytes包含以UTF8编码的XML,并在其开始处带有字节标记。 然后,这将是解决问题的代码:

var stream = new MemoryStream(xmlBytes);
var document = XDocument.Load(stream);

就这么简单。

如果以字符串开头,则应该仍然很容易做到(假设xml是包含带有字节顺序标记的XML的字符串):

var bytes = Encoding.UTF8.GetBytes(xml);
var stream = new MemoryStream(bytes);
var document = XDocument.Load(stream);
Steven Oxley answered 2020-07-31T06:26:42Z
8 votes

遇到此问题后,我写了以下文章。

本质上,不是使用BinaryReader类读取文件内容的原始字节,而是将StreamReader类与特定的构造函数一起使用,该构造函数会自动从我尝试检索的文本数据中删除字节顺序标记字符。

Andrew Thompson answered 2020-07-31T06:27:07Z
5 votes

将字节缓冲区(通过DownloadData)传递给string Encoding.UTF8.GetString(byte[])以获取字符串,而不是将缓冲区作为字符串下载。 您当前的方法可能比修剪字节顺序标记有更多的问题。 除非您按照我的建议正确解码,否则unicode字符可能会被误解,从而导致字符串损坏。

编辑:Martin的答案更好,因为它避免为仍然需要解析的XML分配整个字符串。 我给出的最佳答案适用于不需要解析为XML的常规字符串。

Andrew Arnott answered 2020-07-31T06:27:34Z
3 votes

当我有一个base-64编码的文件转换为字符串时,我遇到了这个问题。 虽然我可以将其保存到文件中然后正确读取,但这是我想到的最好的解决方案,它从文件的StartsWith(byte[])到字符串(略微基于TrueWill的回答):

public static string GetUTF8String(byte[] data)
{
    byte[] utf8Preamble = Encoding.UTF8.GetPreamble();
    if (data.StartsWith(utf8Preamble))
    {
        return Encoding.UTF8.GetString(data, utf8Preamble.Length, data.Length - utf8Preamble.Length);
    }
    else
    {
        return Encoding.UTF8.GetString(data);
    }
}

其中StartsWith(byte[])是逻辑扩展名:

public static bool StartsWith(this byte[] thisArray, byte[] otherArray)
{
   // Handle invalid/unexpected input
   // (nulls, thisArray.Length < otherArray.Length, etc.)

   for (int i = 0; i < otherArray.Length; ++i)
   {
       if (thisArray[i] != otherArray[i])
       {
           return false;
       }
   }

   return true;
}
Timothy answered 2020-07-31T06:27:59Z
3 votes

当然,最好是在仍处于字节数组级别的情况下将其剥离,以避免不必要的子字符串/分配。 但是,如果您已经有一个字符串,这也许是处理此字符串的最简单,最高效的方法。

用法:

            string feed = ""; // input
            bool hadBOM = FixBOMIfNeeded(ref feed);

            var xElem = XElement.Parse(feed); // now does not fail

    /// <summary>
    /// You can get this or test it originally with: Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble())[0];
    /// But no need, this way we have a constant. As these three bytes `[239, 187, 191]` (a BOM) evaluate to a single C# char.
    /// </summary>
    public const char BOMChar = (char)65279;

    public static bool FixBOMIfNeeded(ref string str)
    {
        if (string.IsNullOrEmpty(str))
            return false;

        bool hasBom = str[0] == BOMChar;
        if (hasBom)
            str = str.Substring(1);

        return hasBom;
    }
Nicholas Petersen answered 2020-07-31T06:28:23Z
2 votes
StreamReader sr = new StreamReader(strFile, true);
XmlDocument xdoc = new XmlDocument();
xdoc.Load(sr);
lucasjam answered 2020-07-31T06:28:39Z
0 votes

摆脱UTF-8 BOM序言的另一个通用变体:

var preamble = Encoding.UTF8.GetPreamble();
if (!functionBytes.Take(preamble.Length).SequenceEqual(preamble))
    preamble = Array.Empty<Byte>();
return Encoding.UTF8.GetString(functionBytes, preamble.Length, functionBytes.Length - preamble.Length);
Vinicius answered 2020-07-31T06:28:59Z
translate from https://stackoverflow.com:/questions/1317700/strip-byte-order-mark-from-string-in-c-sharp