バグ:文字列の文字コード変換

Posted: 2012年11月08日

文字列の文字コード変換する際によく書いてしまうバグを紹介します。

プログラムプログラム:

 #include  <stdio.h>
 #include <winnls.h>

 int main(int argc, char *argv[])
 {
    CString strSrc = _T("あいうえお12345");
    WCHAR szDest[128];

    // 変換
    ::MultiByteToWideChar(CP_THREAD_ACP, 0, strSrc, strSrc.GetLength(), szDest, sizeof(szDest));

    //
    // 処理
    //

    return 0;
 }

このプログラムにどのような文字列の文字コード変換関連のバグがあると思いますか?

  • strSrcのサイズが大きい場合は、szDestに収まらない

改善版プログラム改善版プログラム:

 #include  <stdio.h>
 #include <winnls.h>

 int main(int argc, char *argv[])
 {
    CString strSrc = _T("あいうえお12345");
    WCHAR *pszDest;

    int nSize = ::MultiByteToWideChar(CP_THREAD_ACP, 0, strSrc, -1, NULL, 0);
    if (nSize == 0) {
        return -1;
    }
    pszUnicode = new WCHAR[nSize];

    // 変換
    if (::MultiByteToWideChar(CP_THREAD_ACP, 0, strSrc, strSrc.GetLength(), pszUnicode, nSize) == 0) {
        return -1;
    }

    //
    // 処理
    //

    return 0;
 }

解説解説:

MultiByteToWideCharは、第四引数を-1に設定すると、必要なバッファーサイズが取得できます。
取得した長さで領域を確保することで、変換元の文字列の長さがどのような長さでも対応することが可能になります。

ちなみにWideからMultiByteに変換する際は、

WideCharToMultiByte

関数が存在します。
使用方法は、同様のため気をつける点も同様です。

その他に変換を行う場合ことが可能な
方法が存在します。

プログラムプログラム:

mbstowcs/wcstombs

 #include  <stdio.h>
 #include <winnls.h>

 int main(int argc, char *argv[])
 {
    CString strSrc = _T("あいうえお12345");
    WCHAR wszDest[128];
    char szDest[128];

    setlocale( LC_CTYPE, "jpn" );

    // Multi byte -> Wide
    size_t nSize = mbstowcs(wszDest, strSrc, 128);
    if (nSize <= 0) {
        return -1;
    }
    // Wide -> Multi byte
    size_t nSize = wcstombs(szDest, wszDest, 128);
    if (nSize <= 0) {
        return -1;
    }

    //
    // 処理
    //

    return 0;
 }

事前に領域の確保が必要となる。
セキュリティを強化したwcstombs_sなど_sをつけた関数も存在しています。
パフォーマンスが著しく落ちるためどちらを使用するかは検討する余地があります。

プログラムプログラム:

CAtlStringA/CAtlStringW

 #include  <stdio.h>
 #include <winnls.h>

 int main(int argc, char *argv[])
 {
    CString strSrc = _T("あいうえお12345");
    CAtlStringW wstrDest;
    CAtlStringA strDest;

    // Multi byte -> Wide
    wstrDest = strSrc;

    // Wide -> Multi byte
    strDest = wstrDest;

    //
    // 処理
    //

    return 0;
 }

代入を行っただけで変換が行われる。
ATLが必要

プログラムプログラム:

T2W/T2A

 #include  <stdio.h>
 #include <winnls.h>

 int main(int argc, char *argv[])
 {
    CString strSrc = _T("あいうえお12345");
    LPWSTR wszDest;
    LPCSTR szDest;

    USES_CONVERSION;
    // Multi byte -> Wide
    wszDest = T2W(strSrc);

    // Wide -> Multi byte
    szDest = T2A(strSrc);

    //
    // 処理
    //

    return 0;
 }

領域の確保の必要はなく、自動で領域を確保し開放してくれるため(スタック領域に領域を確保するため)
メモリーリークの心配は不要
ATLが必要

USES_CONVERSION_EXなど_EXをつけた関数も存在しており
より安全な関数になっています。

他にも変換することが可能です。

変換型名 説明
A ANSI文字列(マルチバイトコード)
W Unicode文字列(ユニコード文字列)
T 定数_UNICODE が定義されている場合は W それ以外の場合は A
OLE OLE 文字列(BSTR等COMを使用した場合など)

※AやWからBSTRに変換する際は、この方法はあまり推奨されていないようです。

詳しい変換処理についてはこちらを参照してください
http://msdn.microsoft.com/ja-jp/library/87zae4a3.aspx

カテゴリー: バグ, 文字コード | タグ: , , , , | コメント無し »

コメント