厳密なUnicodeプログラミングを行うには、どのような前提条件が必要ですか?
これは、私のコードがchar
型をどこでも使用してはならず、wint_t
およびwchar_t
を処理できる関数を使用する必要があることを意味しますか?
このシナリオでマルチバイト文字シーケンスが果たす役割は何ですか?
これは「厳密なユニコードプログラミング」そのものではなく、実際の経験に関するものであることに注意してください。
私の会社で行ったことは、IBMのICUライブラリの周りにラッパーライブラリを作成することでした。ラッパーライブラリはUTF-8インターフェイスを持ち、ICUを呼び出す必要があるときにUTF-16に変換します。パフォーマンスの問題については、UTF-16インターフェイスも提供しました(独自のデータ型を使用)。
場合によっては特定の問題に注意する必要がありますが、アプリケーションはほとんどそのまま(charを使用して)残ることができます。たとえば、strncpy()の代わりに、UTF-8シーケンスの切断を回避するラッパーを使用します。私たちの場合、これで十分ですが、文字を結合するためのチェックも検討できます。コードポイントの数、書記素の数などをカウントするラッパーもあります。
他のシステムとインターフェイスする場合、カスタムの文字構成を行う必要がある場合があります。そのため、アプリケーションによっては柔軟性が必要になる場合があります。
Wchar_tは使用しません。 ICUを使用すると、移植性の予期しない問題を回避できます(もちろん、他の予期しない問題は回避できません:-)。
C標準(C99)は、ワイド文字とマルチバイト文字を提供しますが、それらのワイド文字が何を保持できるかについての保証がないため、それらの値は多少制限されます。特定の実装については、それらは有用なサポートを提供しますが、コードが実装間を移動できる必要がある場合、それらが有用であるという保証は不十分です。
その結果、Hans van Eckによって提案されたアプローチ(ICU-International Components for Unicode-library)の周りのラッパーを書くこと)は、健全なIMOです。
UTF-8エンコーディングには多くのメリットがありますが、その1つは、データを混乱させない場合(たとえば、切り捨てることによって)、UTF-8の複雑さを完全に認識していない関数によってコピーできることです。エンコーディング。これは、_wchar_t
_には当てはまりません。
Unicodeは完全に21ビット形式です。つまり、UnicodeはU + 0000からU + 10FFFFまでのコードポイントを予約します。
UTF-8、UTF-16、UTF-32形式(UTFはUnicode変換形式を表します- nicode を参照)の便利な点の1つは、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ビットWord)コードセットであり、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ユニバーサルキャラクター名
構文
ユニバーサル文字名:
_\u
_hex-quad
_\U
_hex-quad hex-quad
hex-quad:
16進数16進数16進数16進数16進数7.28 Unicodeユーティリティ_
<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>
_は_size_t
_を定義し、_<wchar.h>
_は_mbstate_t
_を定義し、_<stdint.h>
_は_uint_least16_t
_と_uint_least32_t
_を定義します。 )_<uchar.h>
_ヘッダーは、(再起動可能な)変換関数の最小セットも定義します。
mbrtoc16()
c16rtomb()
mbrtoc32()
c32rtomb()
_\unnnn
_または_\U00nnnnnn
_表記を使用して、識別子に使用できるUnicode文字に関するルールがあります。識別子内のそのような文字のサポートをアクティブにアクティブにする必要がある場合があります。たとえば、GCCでは、識別子でこれらを許可するために_-fextended-identifiers
_が必要です。
MacOS Sierra(10.12.5)は、1つのプラットフォームに名前を付けると、_<uchar.h>
_をサポートしないことに注意してください。
これ [〜#〜] faq [〜#〜] は豊富な情報です。そのページと Joel Spolskyによるこの記事 の間で、あなたは良いスタートを切るでしょう。
私は途中で出会った一つの結論:
_wchar_t
_はWindowsでは16ビットですが、他のプラットフォームでは必ずしも16ビットではありません。 Windowsでは必要な悪だと思いますが、おそらく他の場所では回避できます。 Windowsで重要な理由は、名前に非ASCII文字が含まれるファイルを使用する必要があることです(関数のWバージョンと共に)。
_wchar_t
_文字列をとるWindows APIはUTF-16エンコーディングを想定していることに注意してください。また、これはUCS-2とは異なることに注意してください。サロゲートペアに注意してください。この テストページ には啓発テストがあります。
Windowsでプログラミングしている場合、fopen()
、fread()
、fwrite()
などを使用することはできません。これらは_char *
_のみを取り、don 'UTF-8エンコーディングを理解していません。移植性が苦痛になります。
厳密なUnicodeプログラミングを行うには:
strlen
、strcpy
、...が、対応するワイド文字列wstrlen
、wsstrcpy
、...)マルチバイト文字シーケンスは、UTF-16エンコード(wchar_t
で通常使用されるエンコード)よりも前のエンコードであり、Windows専用のようです。
wint_t
について聞いたことがありません。
最も重要なことは、常にテキストとバイナリデータを明確に区別することです。 Python 3.x str
vs. bytes
またはSQL TEXT
vs. BLOB
のモデルに従うようにしてください。
残念ながら、Cは「ASCII文字」とint_least8_t
の両方にchar
を使用することで問題を混乱させています。あなたは次のようなことをしたいと思うでしょう:
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
sだけが必要です。 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を使用する場合、単純な固定幅文字(ただし、固定幅graphemesではありませんが、すべてのタイプの文字列を変更する必要があります)文字列)。
UTF-16を使用する場合、固定幅文字の仮定と8ビットコード単位の仮定の両方を破棄する必要があり、これが最も難しいシングルバイトエンコーディングからパスをアップグレードします。
クロスプラットフォームではないため、積極的にavoidingwchar_t
をお勧めします。UTF-32の場合もあれば、UTF-16の場合もあります。ユニコード前の東アジアのエンコーディング。 typedefs
を使用することをお勧めします
さらに重要なことは、 TCHAR
を避ける です。
標準ライブラリの実装は信用しません。独自の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;
}
基本的に、メモリ内の文字列を、charではなくwchar_t配列として扱います。何らかの種類のI/O(ファイルの読み取り/書き込みなど)を行う場合、UTF-8(これがおそらく最も一般的なエンコード)を使用してエンコード/デコードできます。 RFCをGoogleで検索するだけです。そのため、メモリ内ではマルチバイトになりません。 1つのwchar_tは1文字を表します。ただし、シリアル化を行う場合、いくつかの文字が複数バイトで表されるUTF-8のようなものにエンコードする必要があります。
また、ワイド文字列用にstrcmpなどの新しいバージョンを作成する必要がありますが、これは大きな問題ではありません。最大の問題は、char配列のみを受け入れるライブラリ/既存のコードとの相互運用です。
また、sizeof(wchar_t)については(必要に応じて4バイトが必要になります)、必要に応じてtypedef/macro hacksを使用して常により大きなサイズに再定義できます。
私が知っていることから、wchar_tは実装に依存しています(この wiki記事 からわかるように)。そして、それはユニコードではありません。