C语言处理中文字符
中文字符的存储
正确地存储中文字符需要解决两个问题。1) 足够长的数据类型,
char 只能处理 ASCII 编码中的英文字符,是因为 char 类型太短,只有一个字节,容纳不下我大中华几万个汉字,要想处理中文字符,必须得使用更长的数据类型。
一个字符在存储之前会转换成它在字符集中的编号,而这样的编号是一个整数,所以我们可以用整数类型来存储一个字符,比如 unsigned short、unsigned int、unsigned long 等。
2) 选择包含中文的字符集
C语言规定,对于汉语、日语、韩语等 ASCII 编码之外的单个字符,也就是专门的字符类型,要使用宽字符的编码方式。常见的宽字符编码有 UTF-16 和 UTF-32,它们都是基于 Unicode 字符集的,能够支持全球的语言文化。
在真正实现时,
- 微软编译器(内嵌于 Visual Studio 或者 Visual C++ 中)采用 UTF-16 编码,使用 2 个字节存储一个字符,用 unsigned short 类型就可以容纳。
- GCC、LLVM/Clang(内嵌于 Xcode 中)采用 UTF-32 编码,使用 4 个字节存储字符,用 unsigned int 类型就可以容纳。
- 对于编号较小的字符,UTF-16 采用两个字节存储;
- 对于编号较大的字符,UTF-16 使用四个字节存储。
- 但是,全球常用的字符也就几万个,使用两个字节存储足以,只有极其罕见,或者非常古老的字符才会用到四个字节。
- 微软编译器使用两个字节来存储 UTF-16 编码的字符,虽然不能囊括所有的 Unicode 字符,但是也足以容纳全球的常见字符了,基本满足了软件开发的需求。
- 使用两个字节存储的另外一个好处是可以节省内存,而使用四个字节会浪费 50% 以上的内存。
你看,不同的编译器可以使用不同的整数类型。
- 如果我们的代码使用 unsigned int 来存储宽字符,那么在微软编译器下就是一种浪费;
- 如果我们的代码使用 unsigned short 来存储宽字符,那么在 GCC、LLVM/Clang 下就不够。
为了解决这个问题,C语言推出了一种新的类型,叫做 wchar_t。
- w 是 wide 的首字母,
- t 是 type 的首字符,
- wchar_t 的意思就是宽字符类型。
wchar_t 的长度由编译器决定:
- 在微软编译器下,它的长度是 2,等价于 unsigned short;
- 在GCC、LLVM/Clang 下,它的长度是 4,等价于 unsigned int。
wchar_t 其实是用 typedef 关键字定义的一个别名,我们会在《C语言typedef:给类型起一个别名》一节中深入讲解,大家暂时只需要记住,wchar_t 在不同的编译器下长度不一样。
wchar_t 类型位于 <wchar.h> 头文件中,它使得代码在具有良好移植性的同时,也节省了不少内存,以后我们就用它来存储宽字符。
上节我们讲到,单独的字符由单引号' '包围,例如'B'、'@'、'9'等;但是,这样的字符只能使用 ASCII 编码,要想使用宽字符的编码方式,就得加上L前缀,例如L'A'、L'9'、L'中'、L'国'、L'。'。
注意,加上L前缀后,所有的字符都将成为宽字符,占用 2 个字节或者 4 个字节的内存,包括 ASCII 中的英文字符。
下面的例子演示了如何存储宽字符(注意引入 <wchar.h> 头文件):
1 2 3 4 5 6 7 8 | wchar_t a = L 'A' ; //英文字符(基本拉丁字符) wchar_t b = L '9' ; //英文数字(阿拉伯数字) wchar_t c = L '中' ; //中文汉字 wchar_t d = L '国' ; //中文汉字 wchar_t e = L '。' ; //中文标点 wchar_t f = L 'ヅ' ; //日文片假名 wchar_t g = L '♥' ; //特殊符号 wchar_t h = L '༄' ; //藏文 |
在以后的编程中,
- 我们将不加L前缀的字符称为窄字符,窄字符使用 ASCII 编码,
- 将加上L前缀的字符称为宽字符,宽字符使用 UTF-16 或者 UTF-32 编码。
宽字符的输出
putchar、printf 只能输出不加L前缀的窄字符,对加了L前缀的宽字符无能为力,我们必须使用 <wchar.h> 头文件中的宽字符输出函数,它们分别是 putwchar 和 wprintf:
- putwchar 函数专门用来输出一个宽字符,它和 putchar 的用法类似;
- wprintf 是通用的、格式化的宽字符输出函数,它除了可以输出单个宽字符,还可以输出宽字符串(稍后讲解)。宽字符对应的格式控制符为%lc。
另外,在输出宽字符之前还要使用 setlocale 函数进行本地化设置,告诉程序如何才能正确地处理各个国家的语言文化。由于大家基础还不够,关于本地化设置的内容我们不再展开讲解,请大家先记住这种写法。
如果希望设置为中文简体环境,
- 在 Windows 下请写作:
1
setlocale
(LC_ALL,
"zh-CN"
);
- 在 Linux 和 Mac OS 下请写作:
1
setlocale
(LC_ALL,
"zh_CN"
);
setlocale 函数位于 <locale.h> 头文件中,我们必须引入它。下面的代码完整地演示了宽字符的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <wchar.h> #include <locale.h> #include <stdio.h> int main() { wchar_t a = L 'A' ; //英文字符(基本拉丁字符) wchar_t b = L '9' ; //英文数字(阿拉伯数字) wchar_t c = L '中' ; //中文汉字 wchar_t d = L '国' ; //中文汉字 wchar_t e = L '人' ; //中文汉字 wchar_t f = L 'ヅ' ; //日文片假名 //将本地环境设置为简体中文 setlocale (LC_ALL, "zh_CN" ); //使用专门的 putwchar 输出宽字符 putwchar(a); putwchar(b); putwchar(c); putwchar(d); putwchar(e); putwchar(f); putwchar(L '\n' ); //只能使用宽字符 //使用通用的 wprintf 输出宽字符 wprintf( L "Wide chars: %lc %lc %lc %lc %lc %lc\n" , //必须使用宽字符串 a, b, c, d, e, f ); printf ( "char sizeof :%d\n" , sizeof ( char )); printf ( "wchar_t sizeof :%d\n" , sizeof ( wchar_t )); return 0; } |
宽字符串
给字符串加上L前缀就变成了宽字符串,它包含的每个字符都是宽字符,一律采用 UTF-16 或者 UTF-32 编码。输出宽字符串可以使用 <wchar.h> 头文件中的 wprintf 函数,对应的格式控制符是%ls。
下面的代码演示了如何使用宽字符串:
1 2 3 4 5 6 7 8 9 10 11 | #include <wchar.h> #include <locale.h> int main() { wchar_t web_url[] = L "https://www.baidu.com" ; const wchar_t * web_name = L "将夜书院" ; //将本地环境设置为简体中文 setlocale (LC_ALL, "zh_CN" ); //使用通用的 wprintf 输出宽字符 wprintf(L "web_url: %ls \nweb_name: %ls\n" , web_url, web_name); return 0; } |
其实,不加L前缀的窄字符串也可以处理中文,它和加上了L前缀的宽字符串有什么区别呢?我们将在下节《C语言到底使用什么编码?谁说C语言使用ASCII码,真是荒谬!》中详细讲解。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)