以下是我发现有效的最基本示例(我知道应该定义例如 UNICODE/_UNICODE):
Linux:
#include <stdio.h>
int main() {
char* str = "Rölf";
printf("%s\n", str);
}
window :
#include <stdio.h>
#include <locale.h>
int main() {
setlocale(LC_ALL, "");
wchar_t* str = L"Rölf";
wprintf(L"%s\n", str);
}
现在,我已经阅读了 one way of going about it基本上是“在任何地方都使用 UTF-8/char,并在进行 API 调用时担心特定于平台的转换”。
那就太好了——让用户提供 char* 作为我的库的输入并“简单地”转换它。所以我尝试了以下基于 this example 的代码片段(我也在其他地方看到过它的变体)。如果这真的有效,那将是惊人的。但事实并非如此:
char* str = u8"Rölf";
int len = mbstowcs(NULL, str, 0) + 1;
wchar_t wstr[len];
mbstowcs(wstr, str, len);
wprintf(L"%s\n", wstr);
我也偶然发现了有关控制台字体的讨论以及渲染错误的原因,因此为了证明这不是控制台问题 - 以下内容也不起作用(好吧 - L""文字起作用。转换后的 u8 文字没有):
MessageBoxW(NULL, wstr, L"Rölf", MB_OK);
我是否误解了转换过程?有没有办法完成这项工作? (不使用例如 ICU)
最佳答案
mbstowcs
函数将字符串以当前语言环境编码 转换为 wchar_t[]
,而不是从 UTF-8(除非编码是 UTF-8)。在 2018 年 4 月之后的 Windows 10 或更高版本中,您实际上可以修复 Windows 以使用 UTF-8 作为普通 char[]
字符串的编码,或者作为全局设置,或者可能通过调用 _setmbcp(65001)
。然而,由于可疑的历史原因,旧版本的 Windows 明确禁止这样做。
无论如何,如果不是因为 MSVC 的 wprintf
中的错误,你称为“Windows”的代码的第二个版本应该可以在任意系统上工作:它们具有 的含义%ls
和 %s
向后用于广泛的 stdio 函数。在标准 C 中,您需要 %ls
来格式化 wchar_t[]
字符串。但实际上根本没有理由在那里使用 wprintf
,事实上 wprintf
是非常有问题的,因为你不能将它与面向字节的 stdio 混合使用(这样做会调用 undefined行为)。更好的做法是:
#include <stdio.h>
#include <locale.h>
int main() {
setlocale(LC_ALL, "");
wchar_t* str = L"Rölf";
printf("%ls\n", str);
}
并且这个版本应该可以在 Windows 和符合标准的 C 实现上正常工作,因为对于面向字节的 printf
函数,MSVC 没有 %s
的含义> 和 %ls
颠倒了。
如果你真的想,你也可以使用你的代码的第三个版本的变体,但你不能使用 mbstowcs
从 UTF-8 转换为 wchar_t
。相反,您需要:
假设
wchar_t
是 Unicode 编码的,并使用您自己的(或第三方库的)UTF-8 解码器将 UTF-8 代码点转换为 Unicode 代码点。但这是一个错误的假设,因为 MSVC 也不符合标准,因为它对wchar_t
使用 UTF-16(C 明确禁止“multi-wchar_t
-characters 因为 mb/wc API 本质上与它们不兼容),而不是 Unicode 代码点值(相当于 UTF-32)。使用您自己的(或第三方库的)UTF-8 解码器从 UTF-8 转换为
uchar32_t
(UTF-32),然后使用c32rtomb
转换为wchar_t[]
。使用
iconv
(POSIX 系统上的标准;在 Windows 上作为第三方库提供)直接从 UTF-8 转换为wchar_t
。
适用于 Windows 10 版本 1803+ 的 UTF8 选项
关于c - 在不使用第 3 方库的情况下基于 C 中的 char* 的多平台 Unicode 处理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51687784/