1

先日プログラムを開発しているときに、ASCII 文字列を Unicode 文字列に変換する必要がありました。ところで、私は Visual Studio 2012 を使用して Windows で作業しています。Win32 関数で奇妙な動作に気付きましたが、これは解決MultiByteToWideCharできませんでした。以下にいくつかのテストコードを書きました。

int main()
{
    /* Create const test string */
    char str[] = "test string";

    /* Create empty wchar_t buffer to hold Unicode form of above string, and initialize (zero) it */
    wchar_t *buffer = (wchar_t*) LocalAlloc(LMEM_ZEROINIT, sizeof(wchar_t) * strlen(str));

    /* Convert str to Unicode and store in buffer */
    int result = MultiByteToWideChar(CP_UTF8, NULL, str, strlen(str), buffer, strlen(str));
    if (result == 0)
        printf("GetLastError result: %d\n", GetLastError());

    /* Print MultiByteToWideChar result, str's length, and buffer's length */
    printf_s(
        "MultiByteToWideChar result: %d\n"
        "'str' length: %d\n"
        "'buffer' length: %d\n",
        result, strlen(str), wcslen(buffer));

    /* Create a message box to display the Unicode string */
    MessageBoxW(NULL, buffer, L"'buffer' contents", MB_OK);

    /* Also write buffer to file, raw */
    FILE *stream = NULL;
    fopen_s(&stream, "c:\\test.dat", "wb");
    fwrite(buffer, sizeof(wchar_t), wcslen(buffer), stream);
    fclose(stream);

    return 0;
}

ご覧のとおり、通常の文字列を取得し、Unicode 文字列を格納するためのバッファーを作成し、変換された Unicode 文字列をバッファーに入れ、いくつかの結果を表示し、バッファーをファイルに書き込みます。

出力:

MultiByteToWideChar result: 11
'str' length: 11
'buffer' length: 16

もう変。関数は C 文字列の正しい数の文字を処理していますがwcslen、出力バッファが C 文字列よりも長いと報告しています! 私もバッファを正しく割り当てたと確信しています。

さまざまなサイズの文字列の長さを使用してみましたが、最後には常にがらくたがあり、wcslen常にバッファーの長さが 4 の倍数であると報告されます。

最後に、この特定の文字列 ( "test string") について、ファイルに出力された生のバッファを次に示します。

74 00 65 00 73 00 74 00 20 00 73 00 74 00 72 00   t.e.s.t. .s.t.r.
69 00 6E 00 67 00 AB AB AB AB AB AB AB AB EE FE   i.n.g...........

(これは 32 バイト、つまり 16 の Unicode 文字です。)

最後の 10 バイトは 5 文字です。4 つのU+ABABと 1 つのU+FEEEは、私には意味がありません。

文字列を変換しようとするたびに、さまざまな量で発生します。

私はちょっとアイデアがありません。誰?

前もって感謝します!

4

3 に答える 3

5
/* Create empty wchar_t buffer to hold Unicode form of above string, and initialize (zero) it */
wchar_t *buffer = (wchar_t*) LocalAlloc(LMEM_ZEROINIT, sizeof(wchar_t) * strlen(str));

ここからが問題の始まりです。strlen(str) の値は無意味です。特に、入力文字列が utf-8 でエンコードされている場合はそうです。通常、off-by-one バグを考慮せずに、長すぎるバッファーが作成されるため、偶然に回避する傾向があります。

しかし、正しい方法で実行することで、そのバグを簡単に回避できたはずです。関数を 2 回呼び出す必要があります。初めて、最後の引数 (cchWideChar) に 0 を渡します。この関数は、バッファの必要なサイズ (バイトではなく文字) を返します。これで、バッファを割り当てて、関数を 2 回目に呼び出したときに正しい値を渡すことができるようになりました

于 2012-10-21T15:46:05.803 に答える
4

(コメントを回答に変換)

長さに末尾のヌル文字を含める必要があります (のstrlen(str) + 1代わりに渡しますstrlen(str))。また、buffer1 つの要素が短すぎます。末尾の null 文字のためのスペースも必要です。

于 2012-10-21T15:33:31.860 に答える
4

他の人がコメントしているように、あなたは基本的に誤用MultiByteToWideChar()しておりwcslen()、null ターミネータを正しく処理していません。を呼び出すときにヌル ターミネータを含めない場合、ヌル ターミネータMultiByteToWideChar()は出力されません。

代わりにこれを試してください:

int main() 
{ 
    /* Create const test string */ 
    char str[] = "test string"; 
    int strLen = strlen(str);

    WCHAR *buffer = NULL;
    int bufLen = 0;

    /* Calculate buffer size */ 
    int result = MultiByteToWideChar(CP_UTF8, NULL, str, strLen, NULL, 0); 
    if (result > 0)
    {
        /* Create buffer to hold Unicode form of above string */ 
        buffer = (WCHAR*) LocalAlloc(LPTR, sizeof(WCHAR) * (result+1)); 
        if (buffer != NULL)
        { 
            /* Convert str to Unicode and store in buffer */ 
            bufLen = result; 
            result = MultiByteToWideChar(CP_UTF8, NULL, str, strLen+1, buffer, bufLen); 
        }
    }

    if ((!buffer) || (result == 0))
        printf("GetLastError result: %d\n", GetLastError());          

    /* Print MultiByteToWideChar result, str's length, and buffer's length */ 
    printf_s( 
        "MultiByteToWideChar result: %d\n" 
        "'str' length: %d\n" 
        "'buffer' length: %d\n", 
        result, strLen, bufLen); 

    /* Create a message box to display the Unicode string */ 
    MessageBoxW(NULL, buffer, L"'buffer' contents", MB_OK); 

    /* Also write buffer to file, raw */ 
    FILE *stream = NULL; 
    errno_t err = fopen_s(&stream, "c:\\test.dat", "wb");
    if (err == 0)
    { 
        fwrite(buffer, sizeof(WCHAR), bufLen, stream); 
        fclose(stream); 
    }
    else
        printf("Errno result: %d\n", err);

    if (buffer)
        LocalFree(buffer);

    return 0; 
} 

C++ を使用しているため、代わりにstd::stringandを使用してメモリ管理を簡素化できます。std:wstring

int main() 
{ 
    /* Create const test string */ 
    std::string str = "test string"; 
    std::wstring buffer;

    /* Calculate buffer size */ 
    int result = MultiByteToWideChar(CP_UTF8, NULL, str.c_str(), str.length(), NULL, 0); 
    if (result > 0)
    {
        /* Allocate buffer to hold Unicode form of above string */ 
        buffer.resize(result); 

        /* Convert str to Unicode and store in buffer */ 
        result = MultiByteToWideChar(CP_UTF8, NULL, str.c_str(), str.length(), &buffer[0], result); 
    }

    if (result == 0)
        printf("GetLastError result: %d\n", GetLastError());          

    /* Print MultiByteToWideChar result, str's length, and buffer's length */ 
    printf_s( 
        "MultiByteToWideChar result: %d\n" 
        "'str' length: %d\n" 
        "'buffer' length: %d\n", 
        result, str.length(), buffer.length()); 

    /* Create a message box to display the Unicode string */ 
    MessageBoxW(NULL, buffer.c_str(), L"'buffer' contents", MB_OK); 

    /* Also write buffer to file, raw */ 
    FILE *stream = NULL; 
    errno_t err = fopen_s(&stream, "c:\\test.dat", "wb");
    if (err == 0)
    { 
        fwrite(buffer.data(), sizeof(std::wstring::value_type), buffer.length(), stream); 
        fclose(stream); 
    }
    else
        printf("Errno result: %d\n", err);

    return 0; 
} 
于 2012-10-21T20:28:35.037 に答える