6

Unicode 対応のクロスプラットフォーム アプリケーションを作成しようとしています。ライブラリ UTF8-C++ ( http://utfcpp.sourceforge.net/ ) を使用していますが、文字列の反復処理に問題があります。

string s1 = "Добрый день";
utf8::iterator<string::iterator> iter(s1.begin(), s1.begin(), s1.end());

for(int i = 0; i < utf8::distance(s1.begin(), s1.end()); i++, ++iter)
{
    cout << (*iter);
}

上記のコードを UTF-8 形式のテキスト ファイルにリダイレクトすると、次の出力が生成されます。

6 3 6 3 6 3 6 3 6 3 6 3 3 2 6 3 6 3 6 3 6 3 

s1のコンテンツをファイルに正しく表示するにはどうすればよいですか?

4

2 に答える 2

9

文字列が正しいデータで初期化され、イテレータが正しい値を生成していることを確認する必要があります。

VS2010 を使用しているため、文字列リテラルに少し問題があります。C++ 実装には、「ソース文字セット」から文字および文字列リテラルを変換する「実行文字セット」があります。Visual Studio は実行文字セットとして UTF-8 をサポートしていないため、UTF-8 でエンコードされた文字列リテラルを意図的に生成することはありません。

コンパイラをだますか、16 進エスケープを使用して取得できます。また、UTF-8 文字列リテラルを取得する代わりに、正しいデータを含むワイド文字列を取得し、実行時にそれを UTF-8 に変換することもできます。


編集: Visual Studio の最近のバージョンには、UTF-8 文字列リテラルを取得する方法が追加されました。Visual Studio 2015 は、C++11 の UTF-8 文字列リテラルをサポートするようになりました。Visual Studio 2015 Update 2 では、コンパイラ フラグ/execution-charset:utf-8 または /utf-8も使用できます。


コンパイラをだます

ソース コードを「署名なしの UTF-8」として保存すると、コンパイラはソース エンコーディングがシステム ロケール エンコーディングであると認識します。VS は常にシステム ロケール エンコーディングを実行エンコーディングとして使用します。したがって、ソースと実行のエンコーディングが同じであると考えられる場合、変換は実行されず、実際には UTF-8 になるソース バイトが文字列リテラルに直接使用されるため、UTF-8 でエンコードされた文字列リテラルが生成されます。(これにより、ワイド文字および文字列リテラルに対して行われた変換が中断されることに注意してください。)

16 進エスケープ

16 進エスケープ コードを使用すると、任意の値のコード単位 (この場合はバイト) を文字列リテラルに手動で挿入できます。必要な UTF-8 エンコーディングを手動で決定し、それらの値を文字列リテラルに挿入できます。

std::string s1 = "\xd0\x94\xd0\xbe\xd0\xb1\xd1\x80\xd1\x8b\xd0\xb9 \xd0\xb4\xd0\xb5\xd0\xbd\xd1\x8c";

UTF-8 文字列リテラル プレフィックス

C++11 は、実行エンコーディングに関係なく UTF-8 文字列リテラルを作成するプレフィックスを指定しますが、Visual Studio はこれをまだ実装していません。これは次のようになります。

string s1 = u8"Добрый день";

コンパイラが正しいソース エンコーディングを認識して使用する必要があります (したがって、ソース エンコーディングが目的の文字列をサポートしている必要があります)。その後、コンパイラはソース エンコーディングから実行エンコーディングではなく UTF-8 への変換を行います。Visual Studio がこの機能をサポートしている場合、ソース コードを「署名付きの UTF-8」として保存することをお勧めします。(ここでも、VS は署名に依存して UTF-8 ソースを識別します。)


UTF-8 文字列を取得したら、UTF-8 イテレータが機能すると仮定すると、サンプル コードは正しい 11 コード ポイントを生成し、出力テキストは次のようになるはずです。

104410861073108810991081321076107710851100

読みやすくするためにいくつかのスペースを挿入すると、正しい値が取得されていることを確認できます。

1044 1086 1073 1088 1099 1081 32 1076 1077 1085 1100

または、16 進数にして Unicode プレフィックスを追加します。

U+0414 U+043e U+0431 U+0440 U+044b U+0439 U+0020 U+0434 U+0435 U+043d U+044c

実際に UTF-8 でエンコードされた出力ファイルを生成したい場合は、とにかく utf-8 イテレータを使用しないでください。

string s1 = "Добрый день";
std::cout << s1;

出力がファイルにリダイレクトされると、ファイルには UTF-8 でエンコードされたデータが含まれます。

Добрый день

現在、実際の出力に大量の余分なスペースが含まれている理由はわかりませんが、アクセスされている実際の数値は次のようです。

63 63 63 63 63 63 32 63 63 63 63

63 は「?」のアスキー コードです。32 はスペースの ASCII コードです。?????? ????. したがって、VC++ による文字列リテラルのシステム ロケール エンコーディングへの変換に明らかに苦しんでいます。

于 2012-08-23T17:19:04.340 に答える
-1

回答が更新されました。UTF16文字列を保存し、UTF8に変換して出力するには、wstring(VS2010が最適だと思います)を使用します。

これは、UTF8 互換のエディター (Scite) で表示するときに機能します。

    std::wstring s1 = L"Добрый день";
    std::vector<unsigned char> UTF8;

    utf8::utf16to8( s1.begin(), s1.end(), std::back_inserter( UTF8 ) );

    for( auto It = UTF8.begin() ; It < UTF8.end() ; ++It )
    {
        std::cout << (*It);
    }

VS2010 で UTF8 リテラルまたは文字列オブジェクトを使用する方法はないと思います。UTF16 (wstring) 内部で最善の策だと思います。その後、UTF8 ライブラリを使用して、ファイル/ネットワークなどにエクスポートするときに UTF8 との間で変換します。 .

于 2012-08-23T16:14:46.233 に答える