[解決済み] C++11でUnicodeはどの程度サポートされていますか?
質問
C++11がUnicodeをサポートしていると読み、聞きました。それについていくつか質問があります。
- C++の標準ライブラリは、どの程度Unicodeをサポートしていますか?
-
は
std::string
はどうすればいいのでしょうか? - どのように使用するのですか?
- 問題が発生しそうな箇所はどこですか?
解決方法は?
<ブロッククオートC++の標準ライブラリは、どの程度ユニコードをサポートしていますか?
ひどいもんだ。
Unicodeをサポートする可能性のあるライブラリ設備をざっと調べてみると、こんなリストが出来上がりました。
- 文字列ライブラリ
- ローカライゼーションライブラリ
- 入出力ライブラリ
- 正規表現ライブラリ
最初のものを除いては、ひどいサポートを提供していると思います。他の質問を少し回り道した後に、より詳しく説明します。
<ブロッククオート
はたして
std::string
はどうすればいいのでしょうか?
はい。C++の規格によると、これは
std::string
とその兄弟が行うべきことです。
クラステンプレート
basic_string
は、任意の数のchar型オブジェクトからなるシーケンスを格納することができるオブジェクトを記述しています。
さて。
std::string
はうまくいきますね。これはUnicode固有の機能を提供しているのでしょうか?いいえ。
そうすべきでしょうか?おそらくないでしょう。
std::string
が連続しても問題ありません。
char
オブジェクトを作成します。これは便利です。唯一の悩みは、これが非常に低レベルのテキスト表示であり、標準C++がより高レベルのものを提供していないことです。
どうやって使うの?
の連続として使用します。
char
他のものだと思い込むと痛い目にあいます。
潜在的な問題はどこにあるのか?
あちこちで?そうですね...
文字列ライブラリ
文字列ライブラリは
basic_string
これは、標準では「char-like objects」と呼ばれているもののシーケンスに過ぎません。私はこれをコードユニットと呼んでいます。もしあなたがテキストのハイレベルなビューを求めているなら、これはあなたが探しているものではありません。これは、シリアライズ/デシリアライズ/ストレージに適したテキストのビューです。
また、狭い世界とUnicodeの世界の橋渡しに使えるCライブラリのツールもいくつか提供されています。
c16rtomb
/
mbrtoc16
と
c32rtomb
/
mbrtoc32
.
ローカライゼーションライブラリ
ローカライゼーションライブラリは、これらの"char-like object"の1つが1つの"character"に等しいとまだ信じているのです。これはもちろん馬鹿げていて、ASCIIのようなUnicodeの小さなサブセットを超えて多くのものを適切に動作させることを不可能にしています。
例えば、この規格で "convenience interfaces" と呼ばれている
<locale>
ヘッダがあります。
template <class charT> bool isspace (charT c, const locale& loc);
template <class charT> bool isprint (charT c, const locale& loc);
template <class charT> bool iscntrl (charT c, const locale& loc);
// ...
template <class charT> charT toupper(charT c, const locale& loc);
template <class charT> charT tolower(charT c, const locale& loc);
// ...
これらの関数で、例えば U+1F34C ʙɴᴀᴀ のように適切に分類できると思いますか?
u8"????"
または
u8"\U0001F34C"
? これらの関数は入力として1つのコードユニットしか取らないので、うまくいくわけがありません。
この場合、適切なロケールであれば
char32_t
だけです。
U'\U0001F34C'
は、UTF-32では1つのコードユニットとなります。
しかし、これでもまだ
toupper
と
tolower
は、例えば、ドイツ語のロケールによっては十分ではありません: "ß" の大文字を "SS"☦ にしますが
toupper
は1つしか返せません。
<ストライク
文字
コード単位。
次のページ
wstring_convert
/
wbuffer_convert
と標準的なコード変換ファセットです。
wstring_convert
は、あるエンコーディングの文字列を別のエンコーディングの文字列に変換するために使用されます。この変換には2つの文字列型があり、標準ではバイト文字列とワイド文字列と呼んでいます。これらの用語は本当に誤解を招くので、私は代わりにそれぞれ "serialized" と "deserialized" を使うことを好んでいます†。
にテンプレート型の引数として渡される codecvt (コード変換ファセット) によって、変換するエンコーディングが決定されます。
wstring_convert
.
wbuffer_convert
は同様の機能を持ちますが
<ストライク
広い
を包むデシリアライズストリームバッファ。
<ストライク
バイト
シリアライズされたストリームバッファです。すべてのI/Oは、基礎となる
バイト
シリアライズされたストリームバッファで、codecvt引数で指定されたエンコーディングに変換されます。書き込みはそのバッファにシリアライズし、そこから書き込みを行い、読み込みはバッファに読み込み、そこからデシリアライズします。
規格では、これらの機能を利用するためのcodecvtクラスのテンプレートがいくつか提供されています。
codecvt_utf8
,
codecvt_utf16
,
codecvt_utf8_utf16
と、いくつかの
codecvt
の特殊化です。これらの標準ファセットは、以下のすべての変換を提供します。(注意: 以下のリストでは、左側のエンコーディングは常にシリアライズされた文字列/streambufであり、右側のエンコーディングは常にデシリアライズされた文字列/streambufです; 標準は両方の方向の変換を許可します)。
-
UTF-8 ↔ UCS-2 で
codecvt_utf8<char16_t>
とcodecvt_utf8<wchar_t>
ここでsizeof(wchar_t) == 2
; -
UTF-8 ↔ UTF-32 の場合
codecvt_utf8<char32_t>
,codecvt<char32_t, char, mbstate_t>
およびcodecvt_utf8<wchar_t>
ここでsizeof(wchar_t) == 4
; -
UTF-16 ↔ UCS-2 で
codecvt_utf16<char16_t>
であり、かつcodecvt_utf16<wchar_t>
ここでsizeof(wchar_t) == 2
; -
UTF-16 ↔ UTF-32 の場合
codecvt_utf16<char32_t>
とcodecvt_utf16<wchar_t>
ここでsizeof(wchar_t) == 4
; -
UTF-8 ↔ UTF-16 で
codecvt_utf8_utf16<char16_t>
,codecvt<char16_t, char, mbstate_t>
およびcodecvt_utf8_utf16<wchar_t>
ここでsizeof(wchar_t) == 2
; -
で狭い↔広い
codecvt<wchar_t, char_t, mbstate_t>
-
を使用することはできません。
codecvt<char, char, mbstate_t>
.
これらのうちいくつかは有用ですが、ここには厄介なものがたくさんあります。
まずオフホーリーハイサロゲート!そのネーミングスキームが雑です。
それから、UCS-2への対応も多いですね。UCS-2はUnicode 1.0のエンコーディングで、基本的な多言語プレーンしかサポートしていないため、1996年に廃止されました。なぜ委員会は20年以上前に置き換えられたエンコーディングに注目することが望ましいと考えたのか、私にはわかりません‡。より多くのエンコーディングをサポートすることが悪いとかいうわけではありませんが、ここではUCS-2があまりにも頻繁に登場するのです。
と言いたいところですが
char16_t
は明らかにUTF-16のコードユニットを格納するためのものです。しかし、これは規格の一部で、そうではないと考えられています。
codecvt_utf8<char16_t>
はUTF-16とは関係ない。例えば
wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")
は正常にコンパイルされますが、無条件に失敗します。
u"\xD83C\xDF4C"
UTF-8は0xD800-0xDFFFの範囲の値をエンコードできないので、UTF-8に変換することはできないのです。
UCS-2に関しては、UTF-16バイトストリームからこれらのファセットを持つUTF-16文字列に読み込む方法はありません。UTF-16バイトの列があったとして、それをデシリアライズして
char16_t
. これは驚くべきことで、多かれ少なかれ同一性変換が行われるからです。しかし,もっと驚くべきことは,UTF-16ストリームからUCS-2文字列へのデシリアライズのために
codecvt_utf16<char16_t>
これは実際には非可逆的な変換です。
エンディアンをBOMから検出したり、コード内で明示的に選択したりすることができます。また、BOMの有無にかかわらず、出力を生成することもできます。
もっと面白い変換の可能性がないわけではありません。UTF-16のバイトストリームや文字列からUTF-8の文字列にデシリアライズする方法はありません。
そして、ここで narrow/wide の世界は、UTF/UCS の世界と完全に分離しています。旧来のnarrow/wideエンコーディングとUnicodeエンコーディングの間の変換は一切ありません。
入出力ライブラリ
I/Oライブラリは、Unicodeエンコーディングでテキストの読み書きを行うために
wstring_convert
と
wbuffer_convert
という施設があります。この標準ライブラリの部分でサポートする必要があるものは、他にはあまりないと思います。
正規表現ライブラリ
の問題点を解説してきました。 C++正規表現とユニコード を以前Stack Overflowで紹介しました。しかし、C++正規表現にはレベル1のUnicodeサポートがなく、あらゆる場所でUTF-32を使用することなく使用できるようにするための最低条件であることを述べたいと思います。
<ブロッククオートそれだけですか?
そうです、それです。それが既存の機能です。正規化とかテキスト分割のアルゴリズムとか、どこにもないUnicodeの機能がたくさんあります。
<ブロッククオートU+1F4A9 . C++でもっと良いUnicodeサポートを得る方法はないのでしょうか?
† バイト列は、当然のことながら、バイトの文字列である。
char
オブジェクトを作成します。ただし
幅の広い文字列リテラル
の配列であり、常に
wchar_t
オブジェクトの文字列ですが、この文脈では、quot; wide string" は必ずしも
wchar_t
オブジェクトを作成します。実際、規格では "wide string" の意味を明示的に定義していないので、使い方から意味を推測することになる。標準の用語は杜撰で分かりにくいので、分かりやすくするために私独自の用語を使っています。
UTF-16のようなエンコーディングは、以下のようなシーケンスとして格納することができます。
char16_t
または、エンディアンを持つバイト列として格納することもできます(連続したバイトの組は、それぞれ異なる
char16_t
の値はエンディアンに依存する)。本標準規格では、これら両方の形式をサポートしている。のシーケンスは
char16_t
は、プログラム内部での操作に便利です。バイト列は、そのような文字列を外部と交換するための方法です。このように、quot;byte" とquot;wide" の代わりに使う用語は、quot;serialized" とquot;deserialized" です。
しかし、Windowsは......」と言いそうになったら、ちょっと待ってください。 ???????? . Windows 2000 以降の Windows はすべて UTF-16 を使用します。
☦ そうなんです。 グローバルエスケット (ǩ)ですが、仮にすべてのドイツ語ロケールを一晩で大文字の ß を ẞ に変更したとしても、失敗するケースは他にたくさんあります。U+FB00 ʟᴀǑɴ sǐᴀʟʟɢᴜʀᴇғғと大文字に変えてみてください。ʟᴀᴛɪᴄᴀᴘɪᴀʟɪⴀᴜ⧑ɫғҝ Fが2つになるだけで、大文字になっています。U+01F0 ʟᴀᴛɪɴ ʟᴇᴊ ᴡɪᴄᴀʀᴏ⇄;は大文字Jと結合カロンまでアッ パーコースティスとなるだけです。
関連
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】Enterキーを押して続行する
-
[解決済み] UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 20: ordinal not in range(128)
-
[解決済み] 文字列リテラルの前にある'b'文字は何を意味するのでしょうか?
-
[解決済み] using namespace std;」はなぜバッドプラクティスだと言われるのですか?
-
[解決済み] コピーアンドスワップ慣用句とは?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] C++11の'typedef'と'using'の違いは何ですか?
-
[解決済み] Pythonのunicode文字列のアクセントを除去(正規化)する最良の方法は何ですか?
-
[解決済み】なぜC++プログラマは'new'の使用を最小限に抑えなければならないのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】Visual Studio 2015で「非標準の構文; '&'を使用してメンバーへのポインターを作成します」エラー
-
[解決済み】'cout'は型名ではない
-
[解決済み】C++の変数はイニシャライザーを持っているが、不完全な型?
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】オブジェクト引数のない非静的メンバ関数の呼び出し コンパイラーエラー
-
[解決済み] 非静的データメンバの無効な使用
-
[解決済み】 while(cin) と while(cin >> num) の違いは何ですか?)
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み] スタックアロケーションにより初期化されていない値が作成された
-
[解決済み】エラー。引数リストに一致するコンストラクタのインスタンスがない