您当前的位置:首页 > 计算机 > 编程开发 > VC/VC++

C语言处理中文字符

时间:12-26来源:作者:点击数:
大部分C语言教材对中文字符的处理讳莫如深,甚至只字不提,导致很多初学者认为C语言只能处理英文,而不支持中文。其实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> 头文件):

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前缀的字符称为窄字符,将加上L前缀的字符称为宽字符。窄字符使用 ASCII 编码,宽字符使用 UTF-16 或者 UTF-32 编码。

宽字符的输出

putchar、printf 只能输出不加L前缀的窄字符,对加了L前缀的宽字符无能为力,我们必须使用 <wchar.h> 头文件中的宽字符输出函数,它们分别是 putwchar 和 wprintf:

  • putwchar 函数专门用来输出一个宽字符,它和 putchar 的用法类似;
  • wprintf 是通用的、格式化的宽字符输出函数,它除了可以输出单个宽字符,还可以输出宽字符串(稍后讲解)。宽字符对应的格式控制符为%lc

另外,在输出宽字符之前还要使用 setlocale 函数进行本地化设置,告诉程序如何才能正确地处理各个国家的语言文化。由于大家基础还不够,关于本地化设置的内容我们不再展开讲解,请大家先记住这种写法。

如果希望设置为中文简体环境,在 Windows 下请写作:

setlocale(LC_ALL, "zh-CN");

在 Linux 和 Mac OS 下请写作:

setlocale(LC_ALL, "zh_CN");

setlocale 函数位于 <locale.h> 头文件中,我们必须引入它。下面的代码完整地演示了宽字符的输出:

#include <wchar.h>
#include <locale.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'ヅ';  //日文片假名
    wchar_t g = L'♥';  //特殊符号
    wchar_t h = L'༄';  //藏文
   
    //将本地环境设置为简体中文
    setlocale(LC_ALL, "zh_CN");
    //使用专门的 putwchar 输出宽字符
    putwchar(a);  putwchar(b);  putwchar(c);  putwchar(d);
    putwchar(e);  putwchar(f);  putwchar(g);  putwchar(h);
    putwchar(L'\n');  //只能使用宽字符
   
    //使用通用的 wprintf 输出宽字符
    wprintf(
        L"Wide chars: %lc %lc %lc %lc %lc %lc %lc %lc\n",  //必须使用宽字符串
        a, b, c, d, e, f, g, h
    );
   
   
    return 0;
}

运行结果:

A9中国。ヅ♥༄
Wide chars: A 9 中 国 。 ヅ ♥ ༄

宽字符串

给字符串加上L前缀就变成了宽字符串,它包含的每个字符都是宽字符,一律采用 UTF-16 或者 UTF-32 编码。输出宽字符串可以使用 <wchar.h> 头文件中的 wprintf 函数,对应的格式控制符是%ls

下面的代码演示了如何使用宽字符串:

#include <wchar.h>
#include <locale.h>
int main(){
    wchar_t web_url[] = L"http://www.cdsy.xyz";
    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;
}

运行结果:

web_url: http://www.cdsy.xyz
web_name: 城东书院

其实,不加L前缀的窄字符串也可以处理中文,它和加上了L前缀的宽字符串有什么区别呢?我们将在下节《C语言到底使用什么编码?谁说C语言使用ASCII码,真是荒谬!》中详细讲解。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门