web-dev-qa-db-ja.com

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を取り除く「正しい」アルゴリズムを提供できますか?

ありがとうございました!

40
TrueWill

変数xmlが文字列型の場合、すでに何か間違ったことをしました-文字列では、BOMは3つの別々の文字としてではなく、単一のコードポイントとして表されるべきです。 DownloadStringを使用する代わりに、DownloadDataを使用し、代わりにバイト配列を解析します。 XMLパーサーはBOM自体を認識し、スキップする必要があります(UTF-8としてエンコードされているドキュメントを自動検出する場合を除く)。

19

最近、.net 4のアップグレードで問題が発生しましたが、それまでの簡単な答えは

String.Trim()

.OM 3.5までBOMを削除します。ただし、.net 4では、わずかに変更する必要があります

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

また、バイトオーダーマークも削除されますが、ZERO WIDTH SPACE 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以降のバージョンでは削除されません。さらに、.NET Framework 3.5 SP1以前のバージョンのTrimメソッドは、3つのUnicode空白文字(MONGOLIAN VOWEL SEPARATOR(U + 180E)、NARROW NO-BREAK SPACE(U + 202F)、およびMEDIUM MATHEMATICAL SPACE)をトリミングしません。 (U + 205F)。

46
PJUK

いくつかの誤ったテストデータがあったため、混乱が生じました。 ファイルの読み取り時に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;
}

クライアントのエンコーディングプロパティを正しく設定すると、BOMが1文字に減ります。ただし、XDocument.Parseはまだその文字列を読み取りません。これは私がこれまでに思いついた最もクリーンなバージョンです。

43
TrueWill

これも機能します

int index = xmlResponse.IndexOf('<');
if (index > 0)
{
    xmlResponse = xmlResponse.Substring(index, xmlResponse.Length - index);
}
30
Vivek Ayer

文字列から直接それを削除するための迅速かつ簡単な方法:

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);
12
Tiago Gouvêa

非常によく似た問題がありました(先頭にバイトオーダーマークが付いたバイト配列として表されるXMLドキュメントを解析する必要がありました)。私は彼の答えに対するMartinのコメントの1つを使用して解決策を見つけました。私は(文字列に変換する代わりに)持っていたバイト配列を取り、それを使ってMemoryStreamオブジェクトを作成しました。それを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);
10
Steven Oxley

この問題に遭遇した後、私は 次の投稿 を書きました。

基本的に、BinaryReaderクラスを使用してファイルのコンテンツの生バイトを読み取る代わりに、取得しようとしているテキストデータからバイトオーダーマーク文字を自動的に削除する特定のコンストラクターでStreamReaderクラスを使用します。

8
Andrew Thompson

バイトバッファーを(DownloadDataを介して)string Encoding.UTF8.GetString(byte[])に渡し、バッファーASをストリングとしてダウンロードするのではなく、ストリングを取得します。現在のメソッドには、単にバイトオーダーマークをトリミングするよりも多くの問題があります。ここで提案するように適切にデコードしない限り、Unicode文字はおそらく誤って解釈され、文字列が破損します。

編集:Martinの答えは、とにかく解析する必要があるXMLに文字列全体を割り当てることを避けるため、より良いです。私が最もよく答えたのは、XMLとして解析する必要のない一般的な文字列です。

5
Andrew Arnott

不要な部分文字列/割り当てを避けるために、バイト配列レベルのままでそれを取り除くことができれば、もちろん最適です。しかし、すでに文字列を持っている場合、これはおそらくこれを処理する最も簡単で最もパフォーマンスの高い方法です。

使用法:

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

文字列に変換する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;
}
_
3
Timothy
StreamReader sr = new StreamReader(strFile, true);
XmlDocument xdoc = new XmlDocument();
xdoc.Load(sr);
2
lucasjam

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);
0
Vinicius