前几天开发程序时,我不得不将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字符串放入缓冲区,并向我显示一些结果,同时将缓冲区写入文件。 / p>
输出:
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个字符;四个U+ABAB和一个U+FEEE,对我来说毫无意义。
以不同的金额,每次 我会尝试转换字符串。
我有点想法。任何人吗?
提前致谢!
答案 0 :(得分: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编码时。你偶然会侥幸逃脱它,因为通常会创建一个太长的缓冲区,而不是计算一个一个错误。
但你也可以通过正确的方式轻松避免这个错误。您必须调用函数两次。第一次,为最后一个参数(cchWideChar)传递0。该函数返回缓冲区所需的大小(字符,而不是字节)。现在,这足以在第二次调用函数时分配缓冲区和传递正确的值。
答案 1 :(得分:4)
(转换评论回答)
您需要在长度中包含尾随空字符(传递strlen(str) + 1
而不是strlen(str)
)。此外,buffer
是一个元素太短 - 它还需要留下空尾字符的空间。
答案 2 :(得分: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;
}