.NET(C#):中国語コードファイルの正確な読み取りについて

5521 ワード

まず、読者がコードやBOMに慣れていない場合は、まずこの文章を読むことをお勧めします.NET(C#):文字コード(Encoding)とバイト順タグ(BOM).中国語の符号化は基本的に2つの種類に分けることができる:1.ANSI符号化の拡張集合:例えばGBK、GB 2312、GB 18030など、これらの符号化にはBOMは存在しない(一部の更新された標準中国語符号化、例えばGB 18030とGBK符号化は、いずれも後方互換GB 2312符号化である).2.Unicode符号化セット:例えばUTF-8、UTF-16、UTF-32など.このような符号化はBOMがあってもよいし、BOMを付けなくてもよい.3.部分Unicode符号化には具体的なバイト順の問題(Endianess)も存在する.いわゆるLittle endianとBig endianの区分であり、この節の順序はUTF 16のような異なるBOMに対して異なるが、UTF 8にはバイト順の問題は存在しない.
OK、基本的な知識を理解したら、テーマに戻って、どのように中国語のテキストファイルを正しく開くかを見てみましょう.最初に確認しなければならない情報は、UnicodeコードファイルにBOMが含まれていますか?
BOMが含まれていれば、すべてが言いやすい!もし私たちがBOMを見つけたら、私たちは彼の具体的なコードを知っていますから.BOMが見つからない場合はUnicodeではなく、システムデフォルトのANSI拡張中国語コードセットでテキストファイルを開くとOKです.UnicodeコードにBOMがない場合(明らかに、ユーザーがあなたに与えたすべてのUnicodeファイルがBOMであることを保証することはできません)、元のバイトから彼がGBKであることを手動で判断しますか?それともUTF 8?それとも他のコードですか.のこれには具体的なコードセンシングアルゴリズムが必要です(google「charset|encoding detection」)もちろん、コードセンシングアルゴリズムが100%正確とは限らないのは、Windows手帳にBush hid the factsバグがあるからです.ChromeでWebページを閲覧すると、文字化けしてしまうこともあります.個人的な感覚では、Notepad++のコードはまだ正確に認識されています.コードセンシングアルゴリズムには、このエンジニアリングのようなものがたくさんあります.https://code.google.com/p/udeUnicodeがBOMを持っていればサードパーティクラスライブラリは不要です.しかし、説明すべき点もいくつかあります.
問題はNETでのテキストの読み取り方法(FileクラスとStreamReader)は、デフォルトではUTF 8符号化で読み取るため、このようなGBKのテキストファイルはそのまま使用する.NETオープン(コード指定しないと)結果は文字化けし!
まず、ここで最も効果的な解決策は、システムデフォルトのANSI拡張符号化、すなわち、システムデフォルトの非Unicode符号化を使用してテキストを読み出すことであり、参照コード:
// Unicode 
Console.WriteLine(Encoding.Default.EncodingName);
// Unicode 
var fileContent = File.ReadAllText("C:\test.txt", Encoding.Default);在简体中文的Windows系统下应该输出: (GB2312)
...

しかもこの方法を使うのは実は簡体字中国語に限らない.
もちろん手動でコードを指定することもできます.例えばGBKコードですが、指定したGBKコードでUnicodeファイルを開くと、ファイルは正常に開きますか?答えは依然として成功している.原因はNETはファイルを開く時にデフォルトで自動的にBOMを感知してからBOMによって得られたコードでファイルを開き、BOMがなければユーザーが指定した符号化領域でファイルを開き、ユーザーが符号化を指定していない場合はUTF 8で符号化する.
この「自動認識BOM」のパラメータは、detectEncodingFromByteOrderMarksパラメータに対応するStreamReaderのコンストラクション関数で設定できます.
ただし、Fileクラスの対応するメソッドでは設定できません.(例:File.ReadAllText).
たとえば、次のコードをそれぞれ使用します.
  • GB 2312符号化、自動認識BOMによりGB 2312テキスト
  • を読み取る.
  • GB 2312符号化、自動認識BOMによりUnicodeテキスト
  • を読み取る.
  • GB 2312は符号化する、BOMに気づかずUnicodeテキスト
  • を読み取る.
    static void Main()
    {
        var gb2312 = Encoding.GetEncoding("GB2312");
        // GB2312 , BOM  GB2312 
        ReadFile("gbk.txt", gb2312, true);
        // GB2312 , BOM  Unicode 
        ReadFile("unicode.txt", gb2312, true);
        // GB2312 , BOM  Unicode 
        ReadFile("unicode.txt", gb2312, false);
    }
    
    // StreamReader 
     static void ReadFile(string path, Encoding enc, bool detectEncodingFromByteOrderMarks)
    {
        StreamReader sr;
        using (sr = new StreamReader(path, enc, detectEncodingFromByteOrderMarks))
        {
            Console.WriteLine(sr.ReadToEnd());
        }
    }

    出力:
    a 
    a 
    ???

    3行目は文字化けしです.
    上を見ると、GB 2312コードを使ってUnicodeファイルを開くのも成功します.「自動認識BOM」のパラメータはTrueであるため、このファイルにBOMがあることを発見すると、NETはBOMでUnicodeファイルだと気づき、Unicodeでファイルを開きます.もちろんBOMがない場合は、指定したエンコードパラメータを使用してファイルを開きます.GB 2312の符号化テキストについては、明らかにBOMがないので、GB 2312の符号化を指定する必要がある、そうでない場合は.NETはデフォルトのUTF 8符号化でファイルを解析しますが、結果は読めません.3行目の文字化けしは「自動感知BOM」がFalseであるためである.NETは指定されたGB 2312符号化でBOMのあるUnicode符号化テキストファイルを直接読み取り、明らかに成功しない.
    もちろん自分でBOMを判断することもできますが、BOMがなければ、デフォルトの符号化を指定してテキストを開くことができます.前の記事で書きました(.NET(C#):ファイルからコードを察知します).
    コード:
    static void Main()
    {
        PrintText("gb2312.txt");
        PrintText("unicode.txt");
    }
    
    // 
    static void PrintText(string path)
    {
        var enc = GetEncoding(path, Encoding.GetEncoding("GB2312"));
        using (var sr = new StreamReader(path, enc))
        {
            Console.WriteLine(sr.ReadToEnd());
        }
    }
    
    /// 
    ///  
    /// 
    ///  
    ///  BOM 
    ///  , null。 , BOM ( BOM)。
    static Encoding GetEncoding(string file, Encoding defEnc)
    {
        using (var stream = File.OpenRead(file))
        {
            // ?
            if (!stream.CanRead)
                return null;
            // BOM
            var bom = new byte[4];
            // 
            int readc;
    
            readc = stream.Read(bom, 0, 4);
    
            if (readc >= 2)
            {
                if (readc >= 4)
                {
                    //UTF32,Big-Endian
                    if (CheckBytes(bom, 4, 0x00, 0x00, 0xFE, 0xFF))
                        return new UTF32Encoding(true, true);
                    //UTF32,Little-Endian
                    if (CheckBytes(bom, 4, 0xFF, 0xFE, 0x00, 0x00))
                        return new UTF32Encoding(false, true);
                }
                //UTF8
                if (readc >= 3 && CheckBytes(bom, 3, 0xEF, 0xBB, 0xBF))
                    return new UTF8Encoding(true);
    
                //UTF16,Big-Endian
                if (CheckBytes(bom, 2, 0xFE, 0xFF))
                    return new UnicodeEncoding(true, true);
                //UTF16,Little-Endian
                if (CheckBytes(bom, 2, 0xFF, 0xFE))
                    return new UnicodeEncoding(false, true);
            }
    
            return defEnc;
        }
    }
    
    // , 
    static bool CheckBytes(byte[] bytes, int count, params int[] values)
    {
        for (int i = 0; i < count; i++)
            if (bytes[i] != values[i])
                return false;
        return true;
    }

    上記のコードでは、Unicodeテキストの場合、GetEncodingメソッドはUTF 16符号化(より具体的には、BOMに基づいてBigまたはLittle-endianのUTF 16符号化も返す)を返し、BOMのないファイルはデフォルト値GB 2312符号化を返す.
    Related Posts:
  • .NET(C#):ファイルから認識符号化
  • .NET(C#):文字コード(Encoding)とバイト順タグ(BOM)
  • .NET(C#):Systemを使用する.Text.Decoderクラスは「ストリームテキスト」
  • を処理する.
  • .NET(C#):プログラムセットリストリソースとRESXリソース