При разработке программы на днях мне пришлось преобразовать строку 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, которые не имеют смысла для меня.
В разных количествах они встречаются каждый раз Я пытаюсь преобразовать строку.
У меня вроде нет идей. Кто-нибудь?
Заранее спасибо!
/* 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). Функция возвращает необходимый размер буфера (символы, а не байты). Который сейчас достаточно хорош, чтобы выделить буфер а также передайте правильное значение во второй раз, когда вы вызываете функцию.
(Конвертированный комментарий в ответ)
Вы должны включить завершающий нулевой символ в длину (проход strlen(str) + 1
вместо strlen(str)
). Также ваш buffer
один элемент слишком короткий — ему также нужно место для завершающего нулевого символа.
Как уже говорили другие, вы в основном злоупотребляете 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;
}