前回のUnicodeStringをJIS(ISO-2022-JP)に変換すると文字が減るの続き。
UnicodeStringをJISコードに変換すると文字が減るという問題です。
AnsiStringT<50220> jis1 = L"文字コード"; //=> 文字コ
Windows 2000におけるWin32APIのWideCharToMultiByte関数の動作がおかしいようです。
WideCharToMultiByte関数は、ISO-2022-JPに変換したときの正しいバイト数を返しません。
なお、Windows XP以降では問題ありませんでした。
次のソースコードは、ユニコード文字列をJIS(ISO-2022-JP)コードに変換したときのバイト数を取得する処理です。
const int codepage = 50220;
UnicodeString WCharSource = L"abcテスト";
int SrcChars = WCharSource.Length();
int DestBytes = WideCharToMultiByte(codepage, 0, WCharSource.c_str(), SrcChars, NULL, 0, NULL, NULL);
WideCharToMultiByte関数は、変換後の文字列を受け取るために必要なバッファのサイズ(バイト数)を返します。
上のコードでは12バイト必要ですが、この関数は「9」を返します。3バイト足りません。
3バイトというと、エスケープシーケンスが思い当たります。
JISコードではエスケープシーケンスを用いて、文字集合を切り替えます。
エスケープシーケンスの計算が正しいか試してみました。
const int codepage = 50222;
UnicodeString WCharSource = L"abcテストabc";
int SrcChars = WCharSource.Length();
int DestBytes = WideCharToMultiByte(codepage, 0, WCharSource.c_str(), SrcChars, NULL, 0, NULL, NULL);
WideCharToMultiByte関数は「12」を返します。6バイト足りません。
const int codepage = 50220;
UnicodeString WCharSource = L"abcテストabcテスト";
int SrcChars = WCharSource.Length();
int DestBytes = WideCharToMultiByte(codepage, 0, WCharSource.c_str(), SrcChars, NULL, 0, NULL, NULL);
WideCharToMultiByte関数は「18」を返します。9バイト足りません。
やはりエスケープシーケンスが考慮されていないようです。
原因がわかりましたので、対策も簡単です。
Windows2000でISO-2022-JPに変換するときはバッファを多めにとればいいのです。
さて、問題のUnicodeStringからAnsiStringT<50220>への変換ですが、
System.pasの_LStrFromPWCharLen関数かCharFromWChar関数で、
文字のバイト数を取得するときに、
OSがWindows2000かつコードページが50220ならば、
バッファのバイト数を増やして文字を取得し、取得後に不要な部分を切り捨てればいいと思います。
とても汚いコードになりそうです。
調べてみると、同じような問題に遭遇している方がいらっしゃいました。
「komatの古往今来: ALM2Thunderbird 1.0の動作OSについて」では、Windows 2000環境で変換に失敗するという問題に遭遇されています。
「Windows API関数のWideCharToMultiByteとMultiByteToWideCharの動作に問題がある」とありますが、おそらく同じ問題でしょう。
Pingback: 黒翼猫のコンピュータ日記 2nd Edition
Pingback: 山本隆の開発日誌
Pingback: C++Builder 2010のIndyでメール送信について « 山本隆の開発日誌