UnicodeはC ++ 11でどの程度サポートされていますか?


183

C ++ 11がUnicodeをサポートしていることを読んだり聞いたりしました。それに関するいくつかの質問:

  • C ++標準ライブラリはどの程度Unicodeをサポートしていますか?
  • ないstd::string何それが必要ですか?
  • どうやって使うの?
  • 潜在的な問題はどこにありますか?

19
「std :: stringは必要な処理を実行しますか?」それは何をすべきだと思いますか?
R.マルティーニョフェルナンデス2013

2
私はutf8のニーズに合わせてutfcpp.sourceforge.netを使用しています。ユニコード文字列のイテレータを提供するシンプルなヘッダーファイル。
fscan 2013年

2
std :: stringは、バイト、つまりUTF-8エンコーディングのコードユニットシーケンスを格納する必要があります。はい、それは最初からそれだけです。utf8everywhere.org
Pavel Radzivilovsky 2013年

3
Unicodeサポートの潜在的な最大の問題は、Unicodeとその情報技術自体での使用にあります。Unicodeは、Unicodeの使用目的には適していません(設計もされていません)。Unicodeは、誰かがどこかで書いたすべての可能なグリフを再現するように設計されています。あるグリフを3または4つの異なる意味で作成したり、3つまたは4つの異なる方法で作成したりするなど、ありそうもない独特のニュアンスもあり得ます。これは、日常の言語で使用するのに役立つことを意図したものではなく、適用可能であったり、簡単にまたは明確に処理されることを意図したものではありません。
デイモン

11
はい、日常の言語で使用するために設計されています。少なくとも私のもの。そしておそらくあなたもそうです。人間のテキストを一般的な方法で処理することは非常に難しい作業であることがわかりました。キャラクターが何であるかを明確に定義することさえ不可能です。一般的なグリフの再現は、Unicode憲章の一部でさえありません。
Jean-Denis Muys 2013

回答:


267

C ++標準ライブラリはどの程度Unicodeをサポートしていますか?

ひどく。

Unicodeサポートを提供する可能性のあるライブラリー機能をざっとスキャンすると、次のリストが得られます。

  • 文字列ライブラリ
  • ローカリゼーションライブラリ
  • 入出力ライブラリ
  • 正規表現ライブラリ

最初のものを除いてすべてがひどいサポートを提供すると思います。他の質問を少し迂回した後で、さらに詳しく説明します。

ないstd::string何それが必要ですか?

はい。C ++標準によると、これはstd::stringその兄弟とその兄弟が行うべきことです。

クラステンプレートbasic_stringは、シーケンスの最初の要素が位置0にある、さまざまな数の任意のcharのようなオブジェクトで構成されるシーケンスを格納できるオブジェクトを記述します。

まあ、それstd::stringはうまくいきますか。Unicode固有の機能はありますか?番号。

よろしいですか?おそらく違います。オブジェクトのstd::stringシーケンスとしては問題ありcharません。それは便利です。唯一の不満は、それがテキストの非常に低レベルのビューであり、標準のC ++が高レベルのビューを提供しないことです。

どうやって使うの?

charオブジェクトのシーケンスとして使用します。それが別のふりをするふりをすることは苦痛で終わるにちがいない。

潜在的な問題はどこにありますか?

あらゆる所に?どれどれ...

文字列ライブラリ

文字列ライブラリはbasic_string、標準が「charのようなオブジェクト」と呼ぶものの単なるシーケンスであるusを提供します。私はそれらをコード単位と呼びます。テキストの高レベルのビューが必要な場合、これはあなたが探しているものではありません。これは、シリアライズ/デシリアライズ/ストレージに適したテキストのビューです。

また、狭い世界とUnicodeの世界の間のギャップを埋めるために使用できるCライブラリのツールもいくつか提供しています:c16rtomb/ mbrtoc16c32rtomb/ mbrtoc32

ローカリゼーションライブラリ

ローカリゼーションライブラリは、これらの「charのようなオブジェクト」の1つが1つの「文字」に等しいとまだ信じています。もちろんこれはばかげているので、ASCIIのようなUnicodeの小さなサブセットを超えて多くのことを適切に機能させることは不可能です。

たとえば、標準で<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 inを適切に分類することを期待しますu8"🍌"u8"\U0001F34C"?これらの関数は入力として1つのコード単位しか使用しないため、機能する方法はありません。

これは、UTF-32の単一のコードユニットchar32_tのみを使用している場合、適切なロケールで動作する可能性がありますU'\U0001F34C'

しかし、それはまだあなただけの単純なケースで変換得ることを意味toupperし、tolower「SS」を「ß」大文字の☦しかし:例えば、いくつかのドイツ語のロケールのために良い十分ではない、toupper一つだけ返すことができ、文字コードユニットを。

次は、wstring_convert/ wbuffer_convertと標準のコード変換ファセットです。

wstring_convertある特定のエンコーディングの文字列を別の特定のエンコーディングの文字列に変換するために使用されます。この変換には2つの文字列タイプが含まれ、標準ではバイト文字列とワイド文字列を呼び出します。これらの用語は本当に誤解を招くので、代わりに「シリアル化」と「非シリアル化」をそれぞれ使用することを好みます†。

変換するエンコーディングは、テンプレートタイプの引数としてに渡されるcodecvt(コード変換ファセット)によって決定されwstring_convertます。

wbuffer_convert同様の機能を実行しますが、バイトのシリアル化されたストリームバッファーをラップするワイドな非シリアル化ストリームバッファーとして機能します。すべてのI / Oは、codecvt引数で指定されたエンコーディングとの間の変換を伴う、基礎となるバイトシリアル化ストリームバッファーを介して実行されます。書き込みは、そのバッファーにシリアル化してから書き込み、読み取りはバッファーに読み取り、逆シリアル化します。

:標準では、これらの施設で使用するためのいくつかのcodecvtクラステンプレートを提供しcodecvt_utf8codecvt_utf16codecvt_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は、基本的な多言語プレーンのみをサポートするため、1996年に置き換えられたUnicode 1.0からのエンコーディングです。委員会が20年以上前に置き換えられたエンコーディングに焦点を合わせることが望ましいと考えた理由は、私にはわかりません‡。それはより多くのエンコーディングのサポートが悪いか何かであるようではありませんが、UCS-2はここに頻繁に現れます。

それchar16_tは明らかにUTF-16コード単位を格納するためのものです。ただし、これは別の方法で考える標準の一部です。codecvt_utf8<char16_t>UTF-16とは何の関係もありません。たとえば、wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")は正常にコンパイルされますが、無条件に失敗します。入力はUCS-2文字列として扱われ、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>。これは、実際には非可逆変換です。

ただし、UTF-16-as-bytesのサポートは非​​常に優れています。BOMからエンディアンを検出したり、コードで明示的に選択したりできます。また、BOMがある場合とない場合の出力の生成もサポートします。

さらに興味深い変換の可能性はありません。UTF-8はデシリアライズされた形式としてサポートされないため、UTF-16バイトストリームまたは文字列をUTF-8文字列にデシリアライズする方法はありません。

そして、ここで、ナロー/ワイドの世界は、UTF / UCSの世界から完全に分離しています。古いスタイルのナロー/ワイドエンコーディングとUnicodeエンコーディングの間の変換はありません。

入出力ライブラリ

I / Oライブラリは、上記のwstring_convertおよびwbuffer_convert機能を使用して、Unicodeエンコーディングでテキストを読み書きするために使用できます。標準ライブラリのこの部分でサポートする必要のある他の多くのものはないと思います。

正規表現ライブラリ

以前に、スタックオーバーフローでのC ++正規表現とUnicodeの問題について説明しました。ここではそれらすべてのポイントを繰り返すことはしませんが、C ++正規表現がレベル1のUnicodeサポートを持たないことだけを述べます。

それでおしまい?

はい、それだけです。それが既存の機能です。正規化やテキストセグメンテーションアルゴリズムのようにどこにも見られない多くのUnicode機能があります。

U + 1F4A9。C ++でより良いUnicodeサポートを取得する方法はありますか?

通常の容疑者:ICUおよびBoost.Locale


†バイト文字列は、当然のことながら、バイトの文字列、つまりcharオブジェクトです。ただし、常にオブジェクトの配列であるワイド文字列リテラルとは異なり、wchar_tこのコンテキストの「ワイド文字列」は必ずしもwchar_tオブジェクトの文字列であるとは限りません。実際、標準では「ワイド文字列」の意味を明示的に定義していないため、使用方法から意味を推測する必要があります。標準的な用語はずさんでわかりにくいので、わかりやすくするために、自分の用語を使用します。

UTF-16のようなエンコーディングはのシーケンスとして格納できchar16_t、エンディアンはありません。または、エンディアンを持つ一連のバイトとして格納することもできます(連続するバイトの各ペアはchar16_t、エンディアンに応じて異なる値を表すことができます)。標準はこれらの形式の両方をサポートします。シーケンスはchar16_t、プログラムの内部操作に役立ちます。バイトシーケンスは、そのような文字列を外部の世界と交換する方法です。したがって、「バイト」や「ワイド」の代わりに使用する用語は、「シリアライズ」および「デシリアライズ」されます。

"「Windowsですが!」あなたの🐎🐎を保持します。Windows 2000以降のすべてのバージョンのWindowsはUTF-16を使用しています。

☦はい、私はEszett(ẞ)のグロスについて知っていますが、すべてのドイツ語のロケールを夜に変更してßの大文字をẞにしても、これが失敗するケースは他にもたくさんあります。大文字のU + FB00ʟᴀᴛɪɴsʟɪɢᴀᴛᴜʀᴇupperғғを試してください。ʟᴀᴛɪɴᴄᴀᴘɪᴛᴀʟʟɪɢᴀᴛᴜʀᴇғғはありません。2つのFを大文字にします。またはU + 01F0ʟᴀᴛɪɴsᴍᴀʟʟʟᴇᴛᴛᴇʀᴊᴡɪᴛʜᴄᴀʀᴏɴ; 事前構成された資本はありません。大文字のJと結合するキャロンだけを大文字にします。


26
読めば読むほど、わからなくなってしまった気分になります。私は2、3か月前にこれらのほとんどを読みましたが、それでもすべてをもう一度発見しているような気がします...少し痛い私の貧弱な脳のためにそれを単純にするために、utf8everywhereに関するこれらのアドバイスはすべてまだ有効です、正しい?ユーザーがシステム設定に関係なくファイルを開いて書き込めるようにしたい場合は、ファイル名を尋ねてstd :: stringに保存すれば、Windowsでもすべてが正常に機能するはずですか?(もう一度)申し訳ありません...
Uflex 2013年

5
@Uflex std :: stringで実際にできることは、それをバイナリBLOBとして扱うことです。適切なUnicode実装では、内部(実装の詳細に深く隠されているため)でも外部エンコーディングでも問題ありません(そうですね、エンコーダー/デコーダーを使用できるようにする必要があります)。
Cat Plus Plus

3
@Uflex多分。あなたが理解していないアドバイスに従うことが良い考えであるかどうかわかりません。
R.マルティーニョフェルナンデス

1
C ++ 2014/17でのUnicodeサポートの提案があります。しかし、それは1年で、おそらく4年後のことであり、現在はほとんど役に立ちません。open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3572.html
graham.reeds

20
@ graham.reedsハハ、ありがとう、でも私はそれを知っていました。「謝辞」セクションを確認してください;)
R. Martinho Fernandes 2013

40

Unicodeは標準ライブラリではサポートされていません(サポートされているという意味での妥当な意味のため)。

std::stringはそれよりも優れていませんstd::vector<char>。Unicode(またはその他の表現/エンコーディング)に完全に気づかず、単にそのコンテンツをblobとして扱います。バイト。

blobを保存して分類するだけでよい場合は、かなりうまくいきます。しかし、Unicode機能が必要になり次第(コードポイントの数、書記素の数)など)が必要になり次第、運が悪くなります。

これについて私が知っている唯一の包括的なライブラリはICUです。ただし、C ++インターフェースはJavaインターフェースから派生したため、慣用的ではありません。


2
どの程度Boost.Locale
Uflex 2013年

11
@Uflex:リンクしたページからこの目標を達成するために、Boost.Localeは最先端のUnicodeおよびローカリゼーションライブラリであるICU-International Components for Unicodeを使用しています。
Matthieu M. 2013

1
Boost.Localeは、他の非ICUのバックエンドをサポートし、ここを参照してください:boost.org/doc/libs/1_53_0/libs/locale/doc/html/...
スーパーフライジョン

@SuperflyJon:正しいですが、同じページによると、非ICUバックエンドのUnicodeのサポートは「厳しく制限されています」。
Matthieu M.

24

Unicode NUL(U + 0000)はUTF-8のnullバイトであり、これがnullの唯一の方法であるため、UTF-8をa std::string(またはa char[]またはchar*)に安全に格納できます。バイトはUTF-8で発生する可能性があります。したがって、UTF-8文字列はすべてのCおよびC ++文字列関数に従って適切に終了し、C ++ iostreamを使用してそれらをスリングすることができます(ロケールがUTF-8である限り、std::coutおよびを含みstd::cerrます)。

std::stringUTF-8 でできないことは、コードポイントで長さを取得することです。std::string::size()文字列の長さをバイトで教えてくれますで示し。これは、UTF-8のASCIIサブセット内にいる場合にのみコードポイントの数と同じです。

コードポイントレベルでUTF-8文字列を操作する必要がある場合(つまり、保存して印刷するだけではない場合)、または内部nullバイトが多い可能性があるUTF-16を処理している場合は、ワイド文字列タイプ。


3
std::stringnullが埋め込まれたiostreamに問題なくスローできます。
R.マルティーニョフェルナンデス2013

3
それは完全に意図されています。まだ動いc_str()ているので全く壊れませんsize()。壊れたAPI(つまり、ほとんどのCの世界のように埋め込まれたnullを処理できないもの)だけが壊れます。
R.マルティーニョフェルナンデス2013

1
埋め込まれたnullは、データをnullで終了するC文字列として返すことになっているc_str()ため壊れc_str()ます。これは、C文字列に埋め込みnullを含めることができないため、不可能です。
uckelman 2013年

4
もう違います。c_str()今度は単にと同じdata()、つまりすべてを返します。サイズを取るAPIはそれを消費する可能性があります。できないAPIはできません。
R.マルティーニョフェルナンデス2013

6
c_str()結果にNUL charのようなオブジェクトが続くことを確認するわずかな違いがありますが、私はそうは思いdata()ません。いいえ、data()今もそうです。(もちろん、ターミネーター検索からサイズを推測する代わりにサイズを消費するAPIの場合、これは必要ありません)
Ben Voigt

8

C ++ 11には、Unicode用の新しいリテラル文字列型がいくつかあります。

残念ながら、標準ライブラリでの非均一エンコーディング(UTF-8など)のサポートはまだ悪いです。たとえば、UTF-8文字列の長さ(コードポイント単位)を取得するための良い方法はありません。


ラテン語以外の言語をサポートする場合でも、ファイル名にstd :: wstringを使用する必要がありますか?新しい文字列リテラルは、通常は文字列がユーザーから来るため、実際には役に立ちません...
Uflex

7
@Uflex std::stringは問題なくUTF-8文字列を保持できlengthますが、たとえば、メソッドはコードポイントの数ではなく、文字列のバイト数を返します。
一部のプログラマーの男

8
正直なところ、文字列のコードポイントで長さを取得することは、あまり役に立ちません。たとえば、バイト単位の長さは、バッファを正しく事前に割り当てるために使用できます。
R.マルティーニョフェルナンデス2013

2
UTF-8文字列のコードポイントの数はそれほど興味深い数ではありません。ñ「ラテン小文字N WITH TILDE」(U + 00F1)(1つのコードポイント)または「小ラテン文字N」( U + 006E)に続いて、2つのコードポイントである 'COMBINING TILDE'(U + 0303)。
Martin Bonnerがモニカの

「重要ではないコードポイントの数」など、「これは必要ない、それは必要ない」などのコメントはすべて、私には少し怪しげに聞こえます。種類のutf8ソースコードを解析することになっているパーサーを書いたら、LATIN SMALL LETTER N' == と見なすかどうかにかかわらず、パーサーの仕様次第(U+006E) followed by 'COMBINING TILDE' (U+0303)です。
BitTickler

4

ただし、tiny-utf8と呼ばれる非常に便利なライブラリがあり、これは基本的に/のドロップイン置換です。まだ足りないutf8-stringコンテナークラスのギャップを埋めることを目的としています。std::stringstd::wstring

これは、utf8文字列を使用して(つまり、ユニコードの正規化などを使用せずに)処理する最も快適な方法です。コードポイントを快適に操作しながら、文字列はランレングスエンコードされたcharsでエンコードされたままです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.