MultiByteToWideChar завершает выходной буфер ненужным, но сообщает об ошибке. Зачем?

При разработке программы на днях мне пришлось преобразовать строку ASCII в строку Unicode. Я работаю на Windows с Visual Studio 2012, кстати. Я заметил странное поведение с функцией 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 символов Юникода.)

10 байтов в конце — это пять символов; четыре U + ABAB, и один U + FEEE, которые не имеют смысла для меня.

В разных количествах они встречаются каждый раз Я пытаюсь преобразовать строку.

У меня вроде нет идей. Кто-нибудь?

Заранее спасибо!

1

Решение

/* 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. Вы склонны сойти с рук случайно, потому что это обычно создает буфер, который слишком длинный, не считая ошибочных ошибок.

Но вы бы также легко избежали этой ошибки, сделав ее в правильном направлении. Вы должны вызвать функцию дважды. В первый раз передайте 0 для последнего аргумента (cchWideChar). Функция возвращает необходимый размер буфера (символы, а не байты). Который сейчас достаточно хорош, чтобы выделить буфер а также передайте правильное значение во второй раз, когда вы вызываете функцию.

5

Другие решения

(Конвертированный комментарий в ответ)

Вы должны включить завершающий нулевой символ в длину (проход strlen(str) + 1 вместо strlen(str)). Также ваш buffer один элемент слишком короткий — ему также нужно место для завершающего нулевого символа.

4

Как уже говорили другие, вы в основном злоупотребляете MultiByteToWideChar() а также wcslen() неправильно обрабатывая нулевые терминаторы. Если вы не включаете нулевой терминатор при вызове 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::string а также 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;
}
4
По вопросам рекламы ammmcru@yandex.ru