成功在Linux下和Windows下都运行起来了,给个例子:
#include <stdio.h>
#include <locale.h>
#include <wchar.h>
int main (void)
{
setlocale (LC_ALL, "");
wprintf (L"你好世界!\n");
}
重点主要是:
- 要设定区域(使用
setlocale()
,否则会乱码) - 宽窄不能混用
细节问题很多,我这里只说结果了,原因可以看后面的参考链接。
不注意细节的话可能会:只能输出英文、什么都不输出、中文乱码/变成问号、有的输出消失。
1. wchar_t和char
首先,char
是一个字节,wchar_t
是多个字节。
wchar_t
字节数不固定,windows是2字节,gcc是4个字节),实际上gcc的wchar_t
实现是int
类型。
2. %ls,%s和 L""
-
先说
L""
:wchar_t wstr[] = L"你好";
为什么要加这个前缀?因为
"你好"
表示的类型是const char*
,加上L
后才表示const wchar_t *
。 -
%s 和%ls的认知误区
初看时很容易误解,以为%s是printf()
用的,%ls是给wprintf()
用的,实则不然。printf()
也可以用%ls
,wprintf()
也可以用%s
举个例子:
#include <stdio.h>
#include <locale.h>
int main (void)
{
setlocale (LC_ALL, "");
// printf ("%ls", "你好"); // 错误写法
printf ("%ls", L"你好");// 你好
}
先别管那个setlocale()
。
第一个注释的,它没有加L,但是前面又用了%ls
,%ls
按宽字符wchar_t
来解读窄字符char
,自然得不到想要的结果。
第二个,后面用了L前缀,表示把一个宽字符字面量的,前面也用了%ls
来正确解读它,正确。
UTF-8将英文按一个字节算,汉字按3个字节算,所以,"你好"
是7个字节(还有个终止符).
L"你好"
,在gcc的实现中是 4 + 4 + 1 = 9个字节。
加不加L只是解读方式不同。
但是也要看清楚了,不要稀里糊涂乱加,
printf(format, ...)
显然format
得是const char*
,不能这样:printf(L"nihao")
,后面的可变参数可以是任意类型,当然可以是宽字符,只不过用宽字符的话前面就要用对应的格式符%ls
。
而wprintf()
的格式字符串就必须要L,后面要不要和printf
一个道理。
3. 设置区域
实测时如果不写setlocale()
是用不了宽字符的,会没有输出 / 乱码。
看其他人说是C99的标准规定。
想了想也确实合理,因为要做宽窄转换,宽窄转换函数会受setlocale()
设置的区域所影响。
4. 宽窄不能混用
#include <stdio.h>
#include <locale.h>
int main (void)
{
setlocale (LC_ALL, "");
printf ("printf\n");
wprintf (L"wprintf\n");
}
运行你就会发现只输出了printf
,第二个wprintf
没有输出。
原因是一个流会和一个宽窄绑定,具体细节不清楚。
用的时候只能用一个宽度的流,剩下的那一个如果也想用,就用宽窄转换函数来辅助。
要获取宽度,使用fwide()
。
(然而实测时,Windows宽窄混用没问题,linux下混用只有一个有效,一言难尽…… )
参考链接
关于wchar_t(这篇比较有用,强推)
关于宽窄不能混用的问题
关于流的实现不能是面向字节 https://man7.org/linux/man-pages/man3/wprintf.3.html
关于setlocale()解决wprintf()不显示