代码之家  ›  专栏  ›  技术社区  ›  TrueWill

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

  •  37
  • TrueWill  · 技术社区  · 16 年前

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

    在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吗?

    谢谢您!

    11 回复  |  直到 7 年前
        1
  •  19
  •   Martin v. Löwis    16 年前

    如果变量xml是string类型的,那么您已经做了一些错误的事情——在字符串中,不应该将bom表示为三个单独的字符,而是一个代码点。不要使用downloadString,而是使用downloadData,并解析字节数组。XML解析器应该识别出BOM本身,并跳过它(除了自动检测文档编码为UTF-8)。

        2
  •  43
  •   PJUK    12 年前

    我最近对.NET 4的升级有问题,但在那之前,简单的答案是

    String.Trim()

    在.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方法删除了.NET Framework 4和更高版本中Trim方法未删除的两个字符:零宽度空间(U+200B)和零宽度不间断空间(U+FEFF)。此外,.NET Framework 3.5 SP1和早期版本中的修剪方法没有修剪三个Unicode空白字符:蒙古语元音分隔符(U+180E)、窄的不间断空格(U+202F)和中等数学空格(U+205F)。

        3
  •  39
  •   Community Mohan Dere    9 年前

    我有一些不正确的测试数据,这让我有些困惑。基于 How to avoid tripping over UTF-8 BOM when reading files 我发现这很管用:

    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;
    }
    

    正确设置客户机编码属性会将物料清单减少到一个字符。但是,xdocument.parse仍然不会读取该字符串。这是我最新推出的最干净的版本。

        4
  •  27
  •   Vivek Ayer    15 年前

    这个也行

    int index = xmlResponse.IndexOf('<');
    if (index > 0)
    {
        xmlResponse = xmlResponse.Substring(index, xmlResponse.Length - index);
    }
    
        5
  •  12
  •   Tiago Gouvêa    13 年前

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

    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);
    
        6
  •  10
  •   Steven Oxley    15 年前

    我遇到了一个非常类似的问题(我需要解析一个XML文档,该文档表示为一个字节数组,其开头有一个字节顺序标记)。我用马丁对他的回答的一个评论来得出一个解决办法。我获取了我拥有的字节数组(而不是将其转换为字符串),并创建了一个 MemoryStream 对象。然后我把它传给 XDocument.Load 像是一种魅力。例如,假设 xmlBytes 包含以utf8编码的XML,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);
    
        7
  •  7
  •   Andrew Thompson    15 年前

    我写了 following post 在遇到这个问题之后。

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

        8
  •  5
  •   Andrew Arnott    16 年前

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

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

        9
  •  3
  •   Timothy    11 年前

    当我有一个base 64编码的文件要转换成字符串时,我遇到了这个问题。虽然我可以将它保存到一个文件中,然后正确地读取它,但这里是我能想到的从 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;
    }
    
        10
  •  2
  •   siva.k    11 年前
    StreamReader sr = new StreamReader(strFile, true);
    XmlDocument xdoc = new XmlDocument();
    xdoc.Load(sr);
    
        11
  •  0
  •   Nicholas Petersen    7 年前

    当然,最好还是在字节数组级别上删除它,以避免不需要的子字符串/分配。但是,如果您已经有了一个字符串,那么这可能是处理这个问题最简单、性能最好的方法。

    用法:

                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 bytes BOM = `[239, 187, 191]`
        /// </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;
        }