C++文字符号化問題の探究と中国語の文字化けしの発生


引用する
従来、C/C++は中国語の文字の処理に頭が回らないことが多い.
主に次の理由があります.
  • ファイル符号化方式の違い
  • システム環境は中国語の解釈に違いがある
  • 異なるコンパイラは標準ライブラリの実現に違いがある
  • この3つは往々にして互いに影響し合い、玄機を隠し、人を狂わせる.
    本文を書く前に多くのブログを調べましたが、中国語の入出力、cout、wcout、fstream、wfstream、乱コードソリューションなどの問題について詳しく答えましたが、多くのブログは一面的です.
    多くのブロガーは、自分が使用している環境について説明しているだけで、どのIDE、どのコンパイラ、どのシステムが使用されているかを明確に示していません.その結果、ブロガーたちは喜んで自分の問題を解決し、分かち合い、自分とブロガーの問題は同じ問題だと思っているが、実際の状況は大きく異なる.
    必要な説明
    テキスト関連のコンパイラとシステム:
  • msvc v120 Windows 8.1
  • mingw 4.8 32bit Windows 8.1
  • g++ 4.8.2 Linux 64bit

  • テストの開始
    テストの前に説明する必要があります.
    A program should not mix output operations on wcout with output operations on cout (or with other narrow-oriented output operations on stdout): Once an output operation has been performed on either, the standard output stream acquires an orientation (either narrow or wide) that can only be safely changed by calling freopen on stdout. —- cplusplus.com
    すなわちcoutとwcoutを混用して出力しないので,以下の例ではcoutまたはwcoutを単独で用いる.
    coutテスト
    次のテストはVisual Studio 2013で行います.
    MSVC、デフォルトエンコーディングGB 2312(ファイル-拡張保存オプションで表示および変更可能)
    <!-- lang: cpp -->
    #include <iostream>
    
    int main() {
        using namespace std;
    
        const char *code = "abc  def";
    
        cout << "abc  def" << endl;
        cout << code << endl;
    
        return 0;
    }
    

    結果
    abc中国語def
    abc中国語def
    いずれも正しく出力されます.
    MSVC、UTF 8(+bom)に符号化を変更
    結果
    abc中国語def
    abc中国語def
    いずれも正しく出力されます.
    MSVC、UTF 8(-bom)に符号化を変更
    結果
    abc涓枃def
    abc涓枃def
    文字化けしが発生する.
    もんだいぶんせき
    ソースファイルの符号化方式が最後の出力に影響するのは、定数テキストがハード符号化方式を採用しているためであり、つまりソースコードの中の中国語が現在のファイルの符号化方式に基づいて対応するバイトコードに直接翻訳されて記憶空間に格納されるからである.
    「中国語」のように、
    GB 2312(Codepage 936)の符号化は:
    D6 D0 CE C4
    UTF 8は、
    E4 B8 AD E6 96 87
    コンソールにもエンコード方式があり、Windowsのcmdについては、その属性の下の現在のコードページを表示することができ、筆者はANSI(936)である.
    GB 2312のバイトコードがコンソールに転送されると、中国語は正常に表示され、署名のないUTF 8のバイトコードが転送されると、中国語が正しく解釈されず、文字化けしてしまう.
    Q:なぜ署名付きUTF 8が正常に表示されるのでしょうか?
    A:実際にUTF 8には署名は全く必要ありません.M$は頭がいいYYでbomヘッダを作ってファイルがUTF 8かどうかを識別しました.そのため、署名付きUTF 8はcmdでUTF 8と認識され、中国語で正常に表示されます.
    コンソールの符号化と関係があるかどうかをさらに確認し、前の例の乱符号化の発生理由を正しく理解するために、結果をテキストファイルに出力するリダイレクトを行うことができます.
    test.exe > test.txt
    符号化を変更できる任意のテキストエディタ(筆者がevereditを使用している)を使用して表示すると、UTF 8で解釈され、正常に表示され、ANSI(936)で解釈され、さっきの文字化けが得られることがわかります.
    次のテストはQtCreatorで行います.
    MinGW,UTF8
    結果
    abc涓枃def
    abc涓枃def
    文字化けしが発生する.
    MinGW,ANSI-936
    結果
    abc中国語def
    abc中国語def
    正しく表示されます.
    次のテストはLinuxのbashで行います.
    g++,UTF8
    結果
    abc中国語def
    abc中国語def
    正しく表示されます.
    g++,gb2312
    結果
    abc▒▒▒▒def
    abc▒▒▒▒def
    文字化けしが発生する.
    Ubuntuは/etc/default/localeを見て、LANG="en_US.UTF-8"を見ることができて、bashがUTF 8のバイトコードを解釈することができることを説明して、gb 2312のは文字化けになりました.
    小結
    プログラムの出力符号化は、正しい結果が得られるように、「表示プログラム」の表示符号化と適合しなければならない.簡単に言えば、ベルを解くにはベルを結ぶ人が必要だ.
    ワイド文字は1文字を複数バイトで表し、中国語はcharで表しても大丈夫、wcharで表しても問題ありません.
    wcoutテスト
    wcout出力wchar_t型のワイド文字、テストコードは以下の通りです.
    <!-- lang: cpp -->
    #include <iostream>
    
    int main() {
        using namespace std;
    
        const wchar_t *code = L"abc  def";
    
        wcout << L"abc  def" << endl;
        wcout << code << endl;
    
        return 0;
    }
    

    MSVCは、上記の符号化にかかわらず
    結果
    abc
    出力は遮断され、最初の数文字だけが出力され、入力ポインタの出力は無効です.
    もんだいぶんせき
    L「abc中国語def」はメモリに次のように表示されます.
    (gb2312) 61 00 62 00 63 00 2d 4e 87 65 64 00 65 00 66 00
    (utf8-bom) 61 00 62 00 63 00 2d 4e 87 65 64 00 65 00 66 00
    (utf8+bom)61 00 62 00 63 00 93 6d 5f e1 83 67 64 00 65 00 66 00
    wcoutはL「abc中国語def」を処理する際、ワイドバイト順に遍歴し、前のabcは問題なく(小端順の最初のバイトは00)、中国語に出会って認識できず、出力がなく、間接的に後続の「出力がない」ことを招いた.
    つまりwcoutは中国語の出力を処理するために使用できません.
    2番目の着信wchar_tポインタは、出力がないことを発見し、前の出力文の中国語の影響ではないかを検証するために、以下のように単独でテストします.
    <!-- lang: cpp -->
    #include <iostream>
    
    int main() {
        using namespace std;
    
        const wchar_t *code = L"abc  def";
    
        wcout << code << endl;
    
        return 0;
    }
    

    結果
    abc
    説明転送wchar_tポインタは、ワイドバイトの英語を正常に出力することができ、00バイト以外の間隔に遭遇すると、後続のすべての出力が無効になります.
    MinGWの結果も同様で、符号化の有無にかかわらず、wcoutが中国語に出会ったらすぐにひざまずいた.
    ブロガーによると、出力前に次の関数を実行したり、wcoutに中国語を認識させるためにグローバル設定を行ったりすることができます.
    <!-- lang: cpp -->
    std::wcout.imbue(std::locale("chs"));
    std::locale::global(std::locale(""));//    
    
  • MSVCでは、問題なく予想される結果に達することができます.
  • MinGWでは、最初の文がruntime_を放出します.Errorは異常にクラッシュし、2番目の文は無効です.
  • Linux g++で大丈夫です.

  • MinGWのlibstdc++はlocaleの実現に理想的ではなく,stlportを用いることでこの問題を回避できるという噂がある.
    まとめ
  • コードがどのような符号化環境にあるかを認識する
  • 文字列に格納されているデータがどのような符号化であるかを認識する
  • どのような符号化スクリーンにデータを転送するかを認識する
  • ベルを解くにはベルを結ばなければならない
  • 特別でない場合はwchar_の使用は推奨されません.tは中国語文字
  • を格納する
    多くの場合、中国語はハードコーディングでプログラムに入っていない.例えば、中国語はネットワークから来てgb 2312で符号化されているが、「スクリーン」はUTF 8しか認識していない.この場合、必要な符号化変換が行われる.boostライブラリのboost::locale::convでは、幅の狭い文字、ansi、utf間の相互変換を実現するために、多くの一般的な変換テンプレート関数が提供されています.