3

プラットフォーム固有の関数を使用せずに UTF-8 文字列を出力することは可能ですか?

#include <iostream>
#include <locale>
#include <string>

using namespace std;

int main()
{
    ios_base::sync_with_stdio(false);
    wcout.imbue(locale("en_US.UTF-8")); // broken on Windows (?)

    wstring ws1 = L"Wide string.";
    wstring ws2 = L"Wide string with special chars \u20AC";  // Euro character

    wcout << ws1 << endl;
    wcout << ws2 << endl;
    wcout << ws1 << endl;
}

次のランタイム エラーが発生します。

'std::runtime_error'
what() のインスタンスをスローした後に呼び出された終了: locale::facet::_S_create_c_locale 名が無効です

行を削除すると、一度wcout.imbue(locale("en_US.UTF-8"));だけws1印刷されます。

別の質問 (「Unicode テキストを cin および cout するにはどうすればよいですか?」) で、Philipp は次のように書いています。MinGWからもそうですか?

ヒントをありがとう!

プラットフォーム:
MinGW/GCC
Windows 7

4

2 に答える 2

6

Windows の mingw 環境で gcc を使用したことはありませんが、収集したところ、C++ ロケールをサポートしていません。

C++ ロケールをサポートしていないため、これはあまり関係ありませんが、参考までに、Windows は他のほとんどのプラットフォームと同じロケール命名スキームを使用していません。同様の language_country.encoding を使用していますが、言語と国はコードではなく、エンコーディングは Windows コード ページ番号です。したがって、ロケールは「English_United States.65001」になりますが、これはサポートされている組み合わせではありません (コード ページ 65001 (UTF-8) は、どのロケールの一部としてもサポートされていません)。

一度だけ印刷される理由ws1は、文字\u20ACが印刷されるときにストリームが失敗し、失敗ビットが設定されるためです。さらに何かが印刷される前に、エラーをクリアする必要があります。


C++11 では、UTF-8 を移植可能に処理するいくつかの機能が導入されましたが、まだすべてがサポートされているわけではなく、追加によって問題が完全に解決されるわけではありません。しかし、現在の状況は次のとおりです。

char16_tとが typedef ではなくネイティブ タイプとして VS でサポートされている場合、標準の codecvt ファセットの特殊化char32_tを使用できます。これは、それぞれ UTF-16 または UTF-32 とUTF-8 (実行文字セットまたはシステムエンコーディング)。現在の VS (および VS11DP) ではこれらの型は typedef のみであり、テンプレートの特殊化は typedef では機能しないため、これはまだ機能しませんが、コードは VS 2010 のヘッダーに既にあり、.codecvt<char16_t,char,mbstate_t>codecvt<char32_t,char,mbstate_t>#ifdef

標準では、サポートされているいくつかの特殊な目的の codecvt ファセット テンプレート、codecvt_utf8、および codecvt_utf8_utf16 も定義しています。前者は、使用するワイド文字型のサイズに応じて、UTF-8 と UCS-2 または UCS-4 のいずれかの間で変換し、後者は、ワイド文字のサイズに関係なく、UTF-8 と UTF-16 コード単位の間で変換します。タイプ。

std::wcout.imbue(std::locale(std::locale::classic(),new std::codecvt_utf8_utf16<wchar_t>()));
std::wcout << L"ØÀéîðüýþ\n";

これにより、wcout にアタッチされているものは何でも、UTF-8 コード単位が出力されます。出力がファイルにリダイレクトされている場合、それを開くと UTF-8 でエンコードされたファイルが表示されます。ただし、Windows のコンソール モデルと標準ストリームの実装方法が原因で、この方法ではコマンド プロンプトに Unicode 文字が正しく表示されません (コンソール出力コード ページを で UTF-8 に設定してもSetConsoleOutputCP(CP_UTF8)) 。 . UTF-8 コード単位は一度に 1 つずつ出力され、コンソールは、渡された各チャンク (つまり、この場合は 1 バイト) が完全で有効なエンコーディングであることを期待して、渡された個々のチャンクを調べます。チャンク内の不完全または無効なシーケンス (この場合、すべてのマルチバイト文字表現のすべてのバイト) は、文字列が表示されるときに U+FFFD に置き換えられます。

iostream を使用する代わりに C 関数putsを使用して UTF-8 でエンコードされた文字列全体を書き出す場合 (およびコンソール出力コード ページが正しく設定されている場合) は、UTF-8 文字列を出力してコンソールに表示することができます。これを行うために、同じ codecvt ファセットを他のいくつかの C++11 convinence クラスで使用できます。

std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> convert;
puts(convert(L"ØÀéîðüýþ\n).to_bytes().c_str());

上記は、wchar_t が UTF-16 であると想定しているため、まだ移植性が高くありません。これは、Windows では当てはまりますが、他のほとんどのプラットフォームでは当てはまらず、標準では必要ありません。(実際、UTF-16 では一部の文字を表すために複数のコード単位が必要であり、標準では、選択したエンコーディングのすべての文字を単一の wchar_t で表現できる必要があるため、技術的に準拠していないと理解しています)。

std::wstring_convert<std::codecvt_utf8<wchar_t>,wchar_t> convert;

上記は UCS-4 と USC-2 を移植可能に処理しますが、UTF-16 を使用するプラットフォームの Basic Multilingual Plane の外では機能しません。

型特性を使用しconditionalて、サイズに基づいてこれら 2 つのファセットを選択しwchar_t、ほとんど機能するものを取得できます。

std::wstring_convert<
    std::conditional<sizeof(wchar_t)==2,std::codecvt_utf8_utf16<wchar_t>,
                                        std::codecvt_utf8<wchar_t>
    >::type,
    wchar_t
> convert;

または、コーディング標準でマクロが許可されている場合は、プリプロセッサ マクロを使用して適切な typedef を定義します。

于 2012-02-10T02:01:20.840 に答える
1

UTF-8 に対する Windows のサポートはかなり貧弱であり、Windows API を使用してそれを行うことは可能ですが、まったく楽しいものではありません。また、あなたの質問は、プラットフォーム固有の機能を使用したくないと指定しています...

「標準 C++」での実行に関しては、プラットフォーム固有のコードを使用せずに Windows で実行できるかどうかはわかりません。ただし、これらのプラットフォームの詳細を抽象化し、移植可能なコードを記述できるようにする、利用可能なサードパーティ ライブラリが多数あります。

最近、Boost.Locale ライブラリの助けを借りて、内部で UTF-8 を使用するようにアプリケーションを更新しました。 http://www.boost.org/doc/libs/1_48_0/libs/locale/doc/html/index.html

そのロケール生成クラスを使用すると、UTF-8 ベースのロケール オブジェクトを生成して、すべての標準ストリームなどに吹き込むことができます。

現在、MinGW-w64 経由で MSVC と GCC の両方でこれを使用しています。確認することを強くお勧めします。はい、残念ながら技術的には「標準 C++」ではありませんが、Boost はほぼどこでも利用でき、実質的に事実上の標準であるため、大きな懸念事項ではないと思います。

于 2012-02-10T18:45:36.663 に答える