以下は、SOの質問とは見なされない場合があります。範囲外の場合は、遠慮なくお立ち寄りください。ここでの質問は、基本的に「C規格を正しく理解しているか、これが正しい方法であるか」です。
C(したがって、C++およびC++ 0x)での文字処理についての私の理解について、明確化、確認、および修正をお願いしたいと思います。まず、重要な観察事項:
移植性とシリアル化は直交する概念です。
ポータブルなものは、C 、、、のようなものunsigned int
ですwchar_t
。シリアル化可能なものは、uint32_t
またはUTF-8のようなものです。「ポータブル」とは、サポートされているすべてのプラットフォームで同じソースを再コンパイルして動作する結果を得ることができることを意味しますが、バイナリ表現は完全に異なる場合があります(または、TCP-伝書鳩など、存在しない場合もあります)。一方、シリアル化可能なものは常に同じ表現になります。たとえば、Windowsデスクトップ、電話、または歯ブラシで読み取ることができるPNGファイルです。ポータブルなものは内部であり、シリアル化可能なものはI/Oを処理します。ポータブルなものは型の安全であり、シリアル化可能なものは型のパンニングが必要です。</ preamble>
Cでの文字処理に関しては、移植性とシリアル化にそれぞれ関連する2つのグループがあります。
wchar_t
、、/ : C標準setlocale()
は「エンコーディング」について何も述べていません; 実際、テキストやエンコーディングのプロパティにはまったく依存しません。「エントリポイントは、システムのすべての文字を保持できる型を取得します。入力文字シーケンスを読み取り、それらを実行可能なwstringにする関数を取得します。その逆も同様です。mbsrtowcs()
wcsrtombs()
main(int, char**)
wchar_t
iconv()
およびUTF-8,16,32:明確に定義された明確な固定エンコーディング間でトランスコードする関数/ライブラリ。iconvによって処理されるすべてのエンコーディングは、1つの例外を除いて、普遍的に理解され、合意されています。
wchar_t
ポータブルな文字タイプを持つCのポータブルでエンコードにとらわれない世界と、決定論的な外の世界との間の架け橋は、WCHAR-TとUTFの間のiconv変換です。
したがって、文字列を常にエンコーディングに依存しないwstringに内部的に格納し、を介してCRTとインターフェイスし、シリアルwcsrtombs()
化に使用する必要がありますiconv()
か?概念的に:
my program
<-- wcstombs --- /==============\ --- iconv(UTF8, WCHAR_T) -->
CRT | wchar_t[] | <Disk>
--- mbstowcs --> \==============/ <-- iconv(WCHAR_T, UTF8) ---
|
+-- iconv(WCHAR_T, UCS-4) --+
|
... <--- (adv. Unicode malarkey) ----- libicu ---+
実際には、これは、プログラムのエントリポイント用に2つの定型ラッパーを作成することを意味します。たとえば、C++の場合です。
// Portable wmain()-wrapper
#include <clocale>
#include <cwchar>
#include <string>
#include <vector>
std::vector<std::wstring> parse(int argc, char * argv[]); // use mbsrtowcs etc
int wmain(const std::vector<std::wstring> args); // user starts here
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
extern "C" int main()
{
setlocale(LC_CTYPE, "");
int argc;
wchar_t * const * const argv = CommandLineToArgvW(GetCommandLineW(), &argc);
return wmain(std::vector<std::wstring>(argv, argv + argc));
}
#else
extern "C" int main(int argc, char * argv[])
{
setlocale(LC_CTYPE, "");
return wmain(parse(argc, argv));
}
#endif
// Serialization utilities
#include <iconv.h>
typedef std::basic_string<uint16_t> U16String;
typedef std::basic_string<uint32_t> U32String;
U16String toUTF16(std::wstring s);
U32String toUTF32(std::wstring s);
/* ... */
これは、純粋な標準C / C ++のみを使用し、iconvを使用してUTFへの明確に定義されたI / Oインターフェイスを使用して、慣用的でポータブル、ユニバーサル、エンコーディングに依存しないプログラムコアを作成する正しい方法ですか?(Unicodeの正規化や発音区別符号の置換などの問題は範囲外であることに注意してください。 (他のコーディングシステムとは対照的に)実際にUnicodeが必要であると判断した後でのみ、専用ライブラリを使用するなど、これらの詳細に対処する必要があります。 libicuのように。)
更新
多くの非常に素晴らしいコメントに続いて、いくつかの所見を追加したいと思います。
アプリケーションで明示的にUnicodeテキストを処理する場合
iconv
は、コアの-conversion部分を作成し、UCS-4で内部的にuint32_t
/char32_t
-stringsを使用する必要があります。Windows:幅の広い文字列を使用することは一般的に問題ありませんが、コンソール(さらに言えば、任意のコンソール)との対話は制限されているようです。これは、実用的なマルチバイトコンソールエンコーディングがサポートされていないようであり、
mbstowcs
本質的に役に立たないためです(その他些細な拡大よりも)。たとえば、Explorer-dropと一緒にワイドストリング引数を受け取ると、GetCommandLineW
+CommandLineToArgvW
が機能します(おそらく、Windows用に別のラッパーが必要です)。ファイルシステム:ファイルシステムにはエンコーディングの概念がないようで、ファイル名としてnullで終了する文字列を使用するだけです。ほとんどのシステムはバイト文字列を取りますが、Windows/NTFSは16ビット文字列を取ります。存在するファイルを検出するとき、およびそのデータを処理するときは注意する必要があります(たとえば
char16_t
、有効なUTF16を構成しないシーケンス(たとえば、裸のサロゲート)は有効なNTFSファイル名です)。標準Cfopen
は、すべての可能な16ビット文字列にマップされる可能性のある変換がないため、すべてのNTFSファイルを開くことはできません。Windows固有の使用_wfopen
が必要になる場合があります。当然の結果として、そもそも「文字」の概念がないため、一般に、特定のファイル名を構成する「文字数」の明確な概念はありません。買い手責任負担。