WindowsでのUnicode合成文字問題
Posted on 2011年10月27日(木) 21:55
Unicodeでは同じ文字を表すのに複数のコードが存在します。
具体的には合成文字(precomposed character)があります。
例えば「が」のような文字を「か」+「゛」で表す方法と「が」そのものとして表す方法があります。
よって、等価な文字列でもコードは異なるという事があり得ます。
Windowsでは基本的に合成文字は使われないようです。
一方iOSでは合成文字が使われます。
iOSではファイル名を扱おうとすると、自動的にUnicodeの正規化が行われますので、あまり意識しなくても等価な文字列は等価なコードになります。
一方でWindowsではそれが行われないようです。
つまり、Windows上では「こんばんは.txt」という全く同じファイル名のファイルが同じフォルダに複数同時に存在出来ます。
(表示上は全く同じだが、実はコードが違う)
プログラムをする上でこの問題は非常にやっかいです。
MultiByteToWideChar等のAPIで正規化が出来ればいいのですが、何故か出来ません。
(dwFlagsの説明を見るとすごく出来そうなのにやってみると何故かできない)
iOSのファイル名をなんとかWindows側に持ってこれないかと、色々捏ねくりまわしていたら以下の方法で出来たので貼っておきます。
非常に回りくどい事をしていて正しいのかどうかは不明です。。もっと良い方法やご意見ありましたら是非コメントを。
(14/03/21追記)
Windows Vista以降では、NormalizeStringというそのままズバリのAPIがあるようです。
(追記ここまで)
//MultiByte(主にUTF-8)をNFC正規化したワイド文字列に変換する int MultiByteToWideCharNFC( UINT CodePage, // コードページ = CP_UTF8を指定 DWORD dwFlags, // 文字の種類を指定するフラグ = NULL を指定 LPCSTR lpMultiByteStr, // 変換する文字列 int cchMultiByte, // 変換する文字列のバイト数。-1で省略可 LPWSTR lpWideCharStr, // 変換先のバッファ int cchWideChar // バッファのサイズ(バイト数でなく文字数) ) { //一旦WideCharに変換 int lengthWideChar = MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,NULL,0); if(!lengthWideChar){ return 0; } TCHAR* wideTemp = new TCHAR[lengthWideChar]; //TCHAR wBufferTemp[_MAX_PATH*10]; if(MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,wideTemp,lengthWideChar) ==0){ delete[] wideTemp; return 0; } //一度MultiByteに戻す、同時に合成文字をNFC正規化 int lengthMultiByte = WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,wideTemp,-1,NULL,0,NULL,NULL); if(!lengthMultiByte){ delete[] wideTemp; return MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,lpWideCharStr,cchWideChar); } char* multibyteTemp = new char[lengthMultiByte]; if(WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,wideTemp,-1,multibyteTemp,lengthMultiByte,NULL,NULL) ==0){ delete[] wideTemp; delete[] multibyteTemp; return MultiByteToWideChar(CodePage,dwFlags,lpMultiByteStr,cchMultiByte,lpWideCharStr,cchWideChar); } //WideCharに戻す int result = MultiByteToWideChar(CP_ACP,0,multibyteTemp,-1,lpWideCharStr,cchWideChar); delete[] wideTemp; delete[] multibyteTemp; return result; }