またまた移植の話です。今度はUNICODEに関する話題です。
UNICODEはWinNT系でNativeとなる文字コードです。一応ASCII(日本語だとS-JIS)コードも使えますが、
内部的にはUNICODEで扱われているらしいので、今の環境だと速度的にはUNICODE側が有利になります。
もう一つ使用される可能性があるのがUTF-8で、これは主にWebやLinuxで日本語を扱うときに
標準の文字コードとなることが多い文字コードです。これらには以下のような特徴があります。
UNICODE
- すべて16bit(実は32bitもあったりするけど)のため、文字検索を行いやすい(順方向、逆方向)
- WinNT系のNativeの文字コードなのでWindows系では速度が向上する
- メモリ内文字列長と表示文字列長の取得が簡単
- ASCII文字列だけだと文字列メモリサイズがちょうど2倍になるのでメモリ効率はよくない
- 半角文字と全角文字の判定がちょっとややこしい
- LittleEndianとBigEndianの種類が分かれることがあるのでBOMの判定とエンディアン変換が必要になることも
S-JIS
- 文字列探索はちょっと難しくなる(両方向とも)
- UNICODEから変換すると変換に失敗するコードが存在する
- 半角文字と全角文字の判定は文字コードのバイト数を使って簡易判別できる
- エスケープシーケンス問題が絡むと判別が難しくなる
UTF-8
- UTF-8はUNICODEをPackしたもの(必ず1:1で対応する)
- 文字によって1~4byteのばらつきがあって扱いづらい(先頭を見れば何byteかは判別できるが)
- ASCIIの範囲内で収まるときはASCIIと同じ文字コードとなるので英語圏には有利
- 文字列探索はS-JISよりも難しくなる(両方向とも) ただし、対象のオクテットが文字コードの先頭かどうかの判断は出来るのでShiftJISより曖昧さはない
- BOMが定義されているとファイル内の文字が即座にUTF-8と判定できるので便利
- Linux系などでは半標準の文字コードなのでLinuxへの移植を考えているときは選択肢に入る
- 旧のランタイム系関数でも処理が一定量可能(これも移植には有利)
VC++を使っていると、UNICODEとMultiByteのコンパイル対応にはマクロを使うことになります。
切り替えに必要な知識としては
- WinAPIは末尾がAとWになる関数で自動的に変更される
- 実文字列はTEXTマクロなどを使って切り替えに対応が必要
- ランタイム系ではtchar.hを使ってマクロによる切り替えが必要
- STLのstringはtypedefなどを使ってstd::stringとstd::wstringの切り替えが必要
- 切り替えができない関数(GetProcAddressなど)には文字コードの注意が必要
- 元々UNICODEしか受け付けない処理(COM系など)にはMultiByteToWideChar/WideCharToMultiByteとlstrcpyW/lstrcpyAの切り替えをうまく使う必要あり
たとえば、stringの切り替えであれば
#include <string> #ifdef _UNICODE typedef std::wstring string; #else typedef std::string string; #endif //_UNICODE
というような感じです。
移植できるようにコードを書いていると、この辺の動作が面倒なんですよね~。
私がこの頃リリースしているやつは両対応で書いた上でUNICODEで出すようにしています。