web-dev-qa-db-ja.com

文字列、u16stringおよびu32string間の変換

私はUnicode文字列型の間で変換する方法を探していましたが、 このメソッド に出会いました。このメソッドを完全に理解していない(コメントがない)だけでなく、この記事は将来、より良いメソッドがあることを暗示しています。

これが最良の方法である場合、それが機能する理由を指摘してください。そうでない場合は、より良い方法の提案を聞きたいと思います。

43
DrYap

mbstowcs()およびwcstombs()は必ずしもUTF-16またはUTF-32に変換されるわけではなく、wchar_tおよびロケールwchar_tエンコーディングに変換されます。すべてのWindowsロケールは2バイトwchar_tおよびUTF-16をエンコードとして使用しますが、他の主要なプラットフォームは4バイトwchar_tをUTF-32(または一部のロケールでは非Unicodeエンコード)で使用します)。シングルバイトエンコーディングのみをサポートするプラットフォームは、1バイトのwchar_tを持つことさえでき、エンコーディングはロケールによって異なります。したがって、wchar_tは、移植性とUnicodeにとって悪い選択であるように思えます。 *

C++ 11には、より優れたオプションがいくつか導入されています。 std :: codecvtの新しい特殊化、新しいcodecvtクラス、および変換に非常に便利な使用のための新しいテンプレート。

まず、codecvtを使用するための新しいテンプレートクラスはstd :: wstring_convertです。 std :: wstring_convertクラスのインスタンスを作成したら、文字列間で簡単に変換できます。

std::wstring_convert<...> convert; // ... filled in with a codecvt to do UTF-8 <-> UTF-16
std::string utf8_string = u8"This string has UTF-8 content";
std::u16string utf16_string = convert.from_bytes(utf8_string);
std::string another_utf8_string = convert.to_bytes(utf16_string);

異なる変換を行うには、異なるテンプレートパラメータが必要です。その1つはcodecvtファセットです。 wstring_convertで簡単に使用できる新しいファセットを次に示します。

std::codecvt_utf8_utf16<char16_t> // converts between UTF-8 <-> UTF-16
std::codecvt_utf8<char32_t> // converts between UTF-8 <-> UTF-32
std::codecvt_utf8<char16_t> // converts between UTF-8 <-> UCS-2 (warning, not UTF-16! Don't bother using this one)

これらの使用例:

std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::string a = convert.to_bytes(u"This string has UTF-16 content");
std::u16string b = convert.from_bytes(u8"blah blah blah");

新しいstd :: codecvt特殊化は、デストラクタが保護されているため、使用が少し難しくなります。これを回避するには、デストラクタを持つサブクラスを定義するか、std :: use_facetテンプレート関数を使用して既存のcodecvtインスタンスを取得します。また、これらの特殊化の問題は、テンプレートの特殊化がtypedefされた型では機能せず、コンパイラーがchar16_tおよびchar32_tをtypedefとして定義するため、Visual Studio 2010で使用できないことです。 codecvtの独自のサブクラスを定義する例を次に示します。

template <class internT, class externT, class stateT>
struct codecvt : std::codecvt<internT,externT,stateT>
{ ~codecvt(){} };

std::wstring_convert<codecvt<char16_t,char,std::mbstate_t>,char16_t> convert16;
std::wstring_convert<codecvt<char32_t,char,std::mbstate_t>,char32_t> convert32;

Char16_t特殊化は、UTF-16とUTF-8の間で変換します。 char32_t特殊化、UTF-32およびUTF-8。

C++ 11によって提供されるこれらの新しい変換には、UTF-32とUTF-16の間で直接変換する方法が含まれていないことに注意してください。代わりに、std :: wstring_convertの2つのインスタンスを結合するだけです。


***** wchar_tとその目的について、Unicodeや移植可能な国際化コードに一般的に使用すべきでない理由を強調するために、メモを追加すると思いました。以下は私の答えの短いバージョンです https://stackoverflow.com/a/11107667/365496

Wchar_tとは何ですか?

wchar_tは、すべてのロケールのcharエンコードをwchar_tに変換できるように定義されています。wchar_tはすべて1つのコードポイントを表します。

タイプwchar_tは、サポートされているロケール(22.3.1)で指定されている最大の拡張文字セットのすべてのメンバーの個別のコードを表すことができる特殊タイプです。 -[basic.fundamental] 3.9.1/5

このnotesでは、wchar_tがすべてのロケールの文字を同時に表すのに十分な大きさである必要があります。つまり、wchar_tに使用されるエンコーディングはロケールによって異なる場合があります。つまり、あるロケールを使用して文字列を必ずしもwchar_tに変換してから、別のロケールを使用してcharに戻すことはできません。

それがwchar_tの実際の主な用途であるように思われるので、そうでない場合は何が良いのだろうかと思うかもしれません。

Wchar_tの本来の意図と目的は、文字列のコード単位からテキストの文字への1対1のマッピングを必要とするように定義することにより、テキスト処理を単純化し、ascii文字列で使用される同じ単純なアルゴリズムを使用できるようにすることでした他の言語で動作します。

残念ながら、wchar_tの要件は、これを実現するために文字とコードポイント間の1対1のマッピングを想定しています。 Unicodeはその仮定を破るので、単純なテキストアルゴリズムにもwchar_tを安全に使用することはできません。

つまり、ポータブルソフトウェアでは、ロケール間のテキストの一般的な表現として、または単純なテキストアルゴリズムの使用を可能にするために、wchar_tを使用できません。

今日のwchar_tはどのような用途ですか?

とにかく、ポータブルなコードのために。 __STDC_ISO_10646__が定義されている場合、wchar_tの値は、すべてのロケールで同じ値を持つUnicodeコードポイントを直接表します。これにより、前述のロケール間変換を安全に実行できます。ただし、ほとんどのUNIXプラットフォームでは定義されていますが、Windowsはすべてのロケールで同じwchar_tロケールを使用しているにもかかわらず、この方法でwchar_tを使用できると判断することだけに頼ることはできません。

Windowsが__STDC_ISO_10646__を定義しない理由は、Windowsがそのwchar_tエンコーディングとしてUTF-16を使用し、UTF-16がU + FFFFより大きいコードポイントを表すためにサロゲートペアを使用するためです。 '__STDC_ISO_10646__の要件を満たしていません。

プラットフォーム固有のコードの場合、wchar_tの方が便利です。 Windowsでは基本的に必要です(たとえば、一部のファイルはwchar_tファイル名を使用せずに開くことはできません)。ただし、私の知る限り、Windowsがこれに該当する唯一のプラットフォームです(したがって、wchar_tは「Windows_char_t」と考えることができます)。

後知恵では、wchar_tは、テキスト処理を単純化するため、またはロケールに依存しないテキストのストレージとしては明らかに有用ではありません。移植可能なコードは、これらの目的のためにそれを使用しようとすべきではありません。

87
bames53

UTF8文字列との間で変換するヘルパー関数を作成しました(C++ 11):

#include <string>
#include <locale>
#include <codecvt>

using namespace std;

template <typename T>
string toUTF8(const basic_string<T, char_traits<T>, allocator<T>>& source)
{
    string result;

    wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
    result = convertor.to_bytes(source);

    return result;
}

template <typename T>
void fromUTF8(const string& source, basic_string<T, char_traits<T>, allocator<T>>& result)
{
    wstring_convert<codecvt_utf8_utf16<T>, T> convertor;
    result = convertor.from_bytes(source);
}

使用例:

// Unicode <-> UTF8
{
    wstring uStr = L"Unicode string";
    string str = toUTF8(uStr);

    wstring after;
    fromUTF8(str, after);
    assert(uStr == after);
}

// UTF16 <-> UTF8
{
    u16string uStr;
    uStr.Push_back('A');
    string str = toUTF8(uStr);

    u16string after;
    fromUTF8(str, after);
    assert(uStr == after);
}
12
dimon4eg