厳密なUnicodeプログラミングを行うにはどのような前提条件が必要ですか?
これは、私のコードがchar
どこでも型を使用してはならず、wint_t
およびを処理できる関数を使用する必要があることを意味しwchar_t
ますか?
そして、このシナリオでマルチバイト文字シーケンスが果たす役割は何ですか?
厳密なUnicodeプログラミングを行うにはどのような前提条件が必要ですか?
これは、私のコードがchar
どこでも型を使用してはならず、wint_t
およびを処理できる関数を使用する必要があることを意味しwchar_t
ますか?
そして、このシナリオでマルチバイト文字シーケンスが果たす役割は何ですか?
回答:
これは「厳密なUnicodeプログラミング」自体ではなく、実際の経験であることに注意してください。
私の会社で行ったことは、IBMのICUライブラリの周りにラッパーライブラリを作成することでした。ラッパーライブラリにはUTF-8インターフェイスがあり、ICUを呼び出す必要がある場合はUTF-16に変換されます。私たちの場合、パフォーマンスへの影響についてはあまり心配していませんでした。パフォーマンスが問題になると、UTF-16インターフェイスも提供しました(独自のデータ型を使用)。
アプリケーションは(charを使用して)ほとんどそのままにしておくことができますが、特定の問題を認識する必要がある場合もあります。たとえば、strncpy()の代わりに、UTF-8シーケンスの切断を回避するラッパーを使用します。私たちの場合、これで十分ですが、文字を組み合わせるためのチェックを検討することもできます。コードポイントの数、書記素の数などをカウントするためのラッパーもあります。
他のシステムとインターフェイスする場合、カスタムの文字構成を行う必要がある場合があるため、(アプリケーションによっては)ある程度の柔軟性が必要になる場合があります。
wchar_tは使用しません。ICUを使用すると、移植性の予期しない問題を回避できます(もちろん、他の予期しない問題は回避できません:-)。
strncpy
適切に使用すると、UTF-8で完全に安全に使用できるということでした。
strcpy
(UTF-8で使用しても安全です)。使用strncpy
している人は、ターゲットバッファが十分に大きいかどうかわからないため、おそらくそうします。そのため、コピーする最大バイト数を渡したいので、実際に無効なUTF-8シーケンスが作成される可能性があります。
C標準(C99)は、ワイド文字とマルチバイト文字を提供しますが、これらのワイド文字が何を保持できるかについての保証がないため、それらの値は多少制限されます。特定の実装に対して、それらは有用なサポートを提供しますが、コードが実装間を移動できる必要がある場合、それらが有用であるという保証は不十分です。
したがって、Hans van Eckによって提案されたアプローチ(ICUのラッパーを作成すること-Unicodeの国際コンポーネント-ライブラリ)は健全なIMOです。
UTF-8エンコーディングには多くのメリットがあります。その1つは、データを(たとえば、データを切り捨てて)混乱させない場合、UTF-8の複雑さを十分に認識していない関数によってコピーできることです。エンコーディング。これは、wchar_t
。の場合とはまったく異なります。
完全なUnicodeは21ビット形式です。つまり、UnicodeはU +0000からU + 10FFFFまでのコードポイントを予約します。
(UTFはUnicodeの変換形式の略-を参照してUTF-8、UTF-16とUTF-32フォーマットについての有用なことの一つUnicodeは)あなたが情報の損失なしに3つの表現の間で変換することができますということです。それぞれが他の人が表現できるものなら何でも表現できます。UTF-8とUTF-16はどちらもマルチバイト形式です。
UTF-8はマルチバイト形式であることがよく知られており、文字列内の任意のポイントから開始して、文字列内の文字の先頭を確実に見つけることができるように注意深い構造を備えています。シングルバイト文字の上位ビットはゼロに設定されています。マルチバイト文字の最初の文字は、ビットパターン110、1110、または11110(2バイト、3バイト、または4バイト文字の場合)のいずれかで始まり、後続のバイトは常に10で始まります。継続文字は常に範囲0x80..0xBF。UTF-8文字は可能な限り最小の形式で表現する必要があるという規則があります。これらのルールの結果の1つは、バイト0xC0および0xC1(また0xF5..0xFF)が有効なUTF-8データに表示されないことです。
U+0000 .. U+007F 1 byte 0xxx xxxx
U+0080 .. U+07FF 2 bytes 110x xxxx 10xx xxxx
U+0800 .. U+FFFF 3 bytes 1110 xxxx 10xx xxxx 10xx xxxx
U+10000 .. U+10FFFF 4 bytes 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
当初、Unicodeは16ビットのコードセットであり、すべてが16ビットのコードスペースに収まることが期待されていました。残念ながら、現実の世界はもっと複雑であり、現在の21ビットエンコーディングに拡張する必要がありました。
したがって、UTF-16は、「基本多言語プレーン」用に設定された単一ユニット(16ビットワード)コードです。つまり、UnicodeコードポイントU + 0000 .. U + FFFFの文字ですが、2ユニット(32ビット)を使用します。この範囲外の文字。したがって、UTF-16エンコーディングで機能するコードは、UTF-8と同様に、可変幅エンコーディングを処理できる必要があります。ダブルユニット文字のコードはサロゲートと呼ばれます。
サロゲートは、Unicode値の2つの特別な範囲からのコードポイントであり、UTF-16のペアのコードユニットの先頭と末尾の値として使用するために予約されています。先行(高)サロゲートはU + D800からU + DBFFとも呼ばれ、後続または低サロゲートはU + DC00からU + DFFFです。それらは文字を直接表すのではなく、ペアとしてのみ表すため、サロゲートと呼ばれます。
もちろん、UTF-32は、単一のストレージユニットで任意のUnicodeコードポイントをエンコードできます。計算には効率的ですが、ストレージには効率的ではありません。
あなたはICUでより多くの情報を見つけることができますおよびUnicodeのWebサイトます。
<uchar.h>
C11標準はルールを変更しましたが、すべての実装が現在(2017年半ば)でも変更に追いついているわけではありません。C11標準は、Unicodeサポートの変更を次のように要約しています。
- Unicode文字と文字列(
<uchar.h>
)(元々はISO / IEC TR 19769:2004で指定されていました)
以下は、機能の最小限の概要です。仕様には次のものが含まれます。
6.4.3ユニバーサル文字名
構文
universal-character-name:
\u
hex-quad
\U
hex-quad hex-quad
hex-quad:
16進数16進数16進数16進数16進数16進数7.28Unicodeユーティリティ
<uchar.h>
ヘッダーは
<uchar.h>
、Unicode文字を操作するためのタイプと関数を宣言します。宣言
mbstate_t
されたタイプは(7.29.1で説明)およびsize_t
(7.19で説明)です。
char16_t
これは、16ビット文字に使用される符号なし整数型であり、
uint_least16_t
(7.20.1.2で説明されている)と同じ型です。そして
char32_t
これは、32ビット文字に使用される符号なし整数型であり、
uint_least32_t
(7.20.1.2でも説明されている)と同じ型です。
(相互参照の翻訳:<stddef.h>
defines size_t
、
<wchar.h>
defines mbstate_t
、<stdint.h>
defines uint_least16_t
and uint_least32_t
。)<uchar.h>
ヘッダーは、(再起動可能な)変換関数の最小セットも定義します。
mbrtoc16()
c16rtomb()
mbrtoc32()
c32rtomb()
\unnnn
or\U00nnnnnn
表記を使用する識別子で使用できるUnicode文字に関する規則があります。識別子内のそのような文字のサポートを積極的にアクティブ化する必要がある場合があります。たとえば、GCCには-fextended-identifiers
これらを識別子で許可するます。
macOS Sierra(10.12.5)は、1つのプラットフォームを挙げれば、をサポートしていないことに注意してください<uchar.h>
。
wchar_t
ここで少し短い友人を売っていると思います。これらのタイプは、Cライブラリが任意のエンコーディング(非Unicodeエンコーディングを含む)でテキストを処理できるようにするために不可欠です。ワイド文字タイプと関数がないと、Cライブラリには、サポートされているすべてのエンコーディングに対して一連のテキスト処理関数が必要になります。KOI-8でエンコードされたテキスト専用のkoi8len、koi8tok、koi8printf、およびUTF-8用のutf8len、utf8tok、utf8printfを想像してください。テキスト。代わりに、私たちは持っているラッキーです1これらの機能のセット(オリジナルASCIIのものをカウントしない): 、wcslen
、wcstok
とwprintf
。
mbstowcs
およびその仲間)を使用して、サポートされているエンコードをに変換することだけwchar_t
です。wchar_t
フォーマットが完了すると、プログラマーはCライブラリが提供するワイドテキスト処理関数の単一セットを使用できます。優れたCライブラリの実装は、ほとんどのプログラマーが必要とするほぼすべてのエンコーディングをサポートします(私のシステムの1つでは、221の固有のエンコーディングにアクセスできます)。
wchar_t
によってサポートされる任意の文字を含むのに十分な幅の実装を保証する必要があります。これは、(おそらく1つの注目すべき例外を除いて)ほとんどの実装が、を使用wchar_t
するプログラムがシステムでサポートされるすべてのエンコーディングを処理できるように十分な幅を確保することwchar_t
を意味します(Microsoftの幅は16ビットしかないため、実装がすべてのエンコーディングを完全にサポートするわけではありません。最も顕著なのはさまざまなUTFエンコーディングですが、それらは例外であり、ルールではありません)。
このFAQは豊富な情報です。そのページとJoelSpolskyによるこの記事の間で、良いスタートを切ることができます。
私が途中で得た1つの結論:
wchar_t
Windowsでは16ビットですが、他のプラットフォームでは必ずしも16ビットである必要はありません。これはWindowsで必要な悪だと思いますが、おそらく他の場所では回避できます。Windowsで重要な理由は、名前にASCII以外の文字が含まれるファイル(およびWバージョンの関数)を使用する必要があるためです。
wchar_t
文字列を受け取るWindowsAPIは、UTF-16エンコーディングを想定していることに注意してください。これはUCS-2とは異なることにも注意してください。サロゲートペアに注意してください。このテストページには、啓蒙的なテストがあります。
あなたは、Windowsにしている番組は、使用できない場合はfopen()
、fread()
、fwrite()
、など彼らだけ取るため、char *
およびUTF-8エンコーディングを理解していません。持ち運びに苦労します。
f*
して友人と仕事char *
上のすべての標準がそう言うのでプラットフォーム-使用wcs*
wchar_tのための代わりに。
厳密なUnicodeプログラミングを行うには:
strlen
、strcpy
、...しかし、彼らは、widestring対応をwstrlen
、wsstrcpy
、...) マルチバイト文字シーケンスは、UTF-16エンコーディング(通常はで使用されるエンコーディング)よりも前のエンコーディングです。 wchar_t
Windowsのみであるように思われます。
聞いたことがないwint_t
。
最も重要なことは、テキストとバイナリデータを常に明確に区別することです。モデルに従うようにしてくださいPythonの3.xのstr
対bytes
またはSQLのTEXT
対をBLOB
。
残念ながら、Cはchar
「ASCII文字」との両方にを使用して問題を混乱させていint_least8_t
ます。次のようなことをしたいと思うでしょう:
typedef char UTF8; // for code units of UTF-8 strings
typedef unsigned char BYTE; // for binary data
UTF-16およびUTF-32コードユニットのtypedefも必要になる場合がありますが、のエンコーディングがwchar_t
定義されていないため、これはより複雑です。プリプロセッサだけが必要#if
です。CおよびC ++ 0xのいくつかの便利なマクロは次のとおりです。
__STDC_UTF_16__
—定義されている場合、タイプは_Char16_t
存在し、UTF-16です。__STDC_UTF_32__
—定義されている場合、タイプは_Char32_t
存在し、UTF-32です。__STDC_ISO_10646__
—定義されている場合、wchar_t
UTF-32です。_WIN32
— Windowsでは、wchar_t
これは標準に違反していますが、UTF-16です。WCHAR_MAX
—のサイズを決定するために使用できますがwchar_t
、OSがUnicodeを表すためにそれを使用するかどうかは決定できません。これは、私のコードがどこでもchar型を使用してはならず、wint_tとwchar_tを処理できる関数を使用する必要があることを意味しますか?
参照:
いいえ。UTF-8は、char*
文字列を使用する完全に有効なUnicodeエンコーディングです。プログラムが非ASCIIバイトに対して透過的である場合(たとえば、\r
およびに作用する行末コンバーター)、\n
が、そのまま他の文字を通過する)、あなたはすべての変更を加えないする必要があります!
UTF-8を使用する場合は、char
=文字(toupper
ループで呼び出さないなど)またはすべての仮定を変更する必要があります。char
画面列(テキストの折り返しなど)の。
UTF-32を使用すると、固定幅の文字が単純になります(ただし、固定幅の書記素は使用できません)。が、すべての文字列のタイプを変更する必要があります)。
UTF-16を使用する場合は、固定幅文字の仮定と、このシングルバイトエンコーディングから最も困難なアップグレードパスになり、8ビットのコード単位の仮定を、。
クロスプラットフォームではないため、積極的に回避する wchar_t
ことをお勧めします。UTF-32の場合もあれば、UTF-16の場合もあり、Unicode以前の東アジアのエンコーディングである場合もあります。使用をお勧めしますtypedefs
さらに重要なのは、を避けることTCHAR
です。
char *
、const char *
私が覚えている最後に渡された場合、aを取る関数は問題を抱えることがあります(しかし、私はこれについて漠然としていて、どの関数が塩のピンチでそれを取るので)。他の言語でより複雑だからといって、それが悪いデザインであるとは限りません。
私は標準ライブラリの実装を信用しません。独自のUnicodeタイプをロールするだけです。
#include <windows.h>
typedef unsigned char utf8_t;
typedef unsigned short utf16_t;
typedef unsigned long utf32_t;
int main ( int argc, char *argv[] )
{
int msgBoxId;
utf16_t lpText[] = { 0x03B1, 0x0009, 0x03B2, 0x0009, 0x03B3, 0x0009, 0x03B4, 0x0000 };
utf16_t lpCaption[] = L"Greek Characters";
unsigned int uType = MB_OK;
msgBoxId = MessageBoxW( NULL, lpText, lpCaption, uType );
return 0;
}
基本的に、メモリ内の文字列wchar_t
をcharではなく配列として扱いたいと考えています。あらゆる種類のI / O(ファイルの読み取り/書き込みなど)を実行する場合、実装が簡単なUTF-8(おそらく最も一般的なエンコード)を使用してエンコード/デコードできます。RFCをグーグルで検索してください。したがって、メモリ内にはマルチバイトはありません。1つwchar_t
は1つの文字を表します。ただし、シリアル化に関しては、一部の文字が複数のバイトで表されるUTF-8のようなものにエンコードする必要がある場合です。
またstrcmp
、ワイド文字列用に新しいバージョンなどを作成する必要がありますが、これは大きな問題ではありません。最大の問題は、char配列のみを受け入れるライブラリ/既存のコードとの相互運用です。
そしてそれは sizeof(wchar_t)
(正しく実行したい場合は4バイトが必要になります)、必要に応じてtypedef
/ macro
hacksを使用していつでもより大きなサイズに再定義できます。
私の知る限り、wchar_tは実装に依存しています(このwikiの記事からわかるように)。そしてそれはユニコードではありません。