一 概述
二 类型
三 语句
四 函数
五 数据
六 内存
七 代码
附录
2. 字符串
年代久的语言在字符串处理上都有沉重负担,这需要去了解一下 ASCII 和 Unicode 背景。同时,应该抛弃一些老旧的概念(ANSI),统一使用基于 UCS/UTF 的执行和存储方案。
ASCII (美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,它主要用于显示现代英语。 Unicode 是为了解决跨语言、跨平台转换和处理需求,用统一编码方案容纳不同国家地区的文字,以解决传统编码方案不兼容问题,故又称作统一码、万国码。
Unicode 为每个字符分配一个称作 码点(code point) 的整数序号,对应方案叫做通用字符集(Universal Character Set, UCS)。依编码整数长度,分作 UCS-2 和 UCS-4 两种。UCS-4 采用 32 位整数,可容纳更多字符。 UCS 只规定了字符与码点的对应关系,并不涉及存储和显示。 UTF (Unicode Transformation Format)的作用是将码点整数转换为可存储和传输的字节格式,有 UTF-8、UTF-16、UTF-32 等多种方案。
其中 UTF-16、UTF-32 以码点为索引,可简单认为 UCS-2/UTF-16、UCS-4/UTF-32 意思相同。至于 UTF-8,采用变长格式。因与 ASCII 兼容,当下使用最为广泛。它对于英文为主的文本内容,有最高的存储效率。相比之下,等宽的 UTF-16、UTF-32 有更快的处理效率,故常被用作执行编码。
可在头部插入字节序标记(byte order mark,BOM),区分大小端(BE、LE)。故此,又可细分为 UTF-16LE、UTF-32BE 等。
字符
相比单字节 char
,宽字符整数类型 wchar_t
足以容纳 UCS-4 码点。
char c = '我'; ^~~ warning: overflow in conversion from 'int' to 'char' wchar_t c = L'我'; assert(sizeof(c) == 4);
类型前缀:
L
:wchar_t
(int
),Unicode Code Point。u8
:char
, UTF-8。u
:char16_t
(unsigned short
), UTF-16,。U
:char32_t
(unsigned int
), UTF-32,。
支持转义,比如
'\n'
,'\x6c49'
,'\u6c49'
。
#include <uchar.h> char32_t c = U'汉'; assert(c == 0x6c49);
将码点转换为不同 UTF 编码。
#include <stdlib.h> #include <string.h> #include <locale.h> #include <uchar.h> int main (void) { setlocale(LC_ALL, "en_US.UTF-8"); // WCHAR to UTF-8 char mb[10] = { 0 }; int n = wctomb(mb, L'汉'); assert(n == 3); assert(strcmp(mb, "汉") == 0); // UTF-8 to UTF-16 char16_t out; mbstate_t state; size_t ns = mbrtoc16(&out, mb, n, &state); assert(ns == 3); assert(out == u'汉'); return 0; }
字符串
抛弃掉种种因历史造成的混乱定义不说,可简单归纳为:
byte string
: 单字节,ASCII 字符串。wide string
: 宽字符,Unicode 字符串。multibyte string
: 多字节存储,默认 UTF-8 编码。
执行 存储 执行 +=============+ +==================+ +=============+ | byte string | <-----> | multibyte string | <-----> | wide string | +-------------+ +==================+ +-------------+ | ASCII | | | Unicode | +=============+ | +=============+ +=======+========+========+ | UTF-8 | UTF-16 | UTF-32 | +=======+========+========+
执行是对文本进行处理。比如 '我' 应该是长度为 1 的字符,而非存储方案里的几个字节。对于字符,可判断它是字母、数字,还是大小写等。因此,执行面向字符(characters),而存储基于字节(bytes)。
所有字符串都是以 NULL
结尾的字符数组。字面量默认 UTF-8 编码,可添加类型前缀标示。
char s[] = "a 汉"; assert(sizeof(s) == 5); // + NULL assert(strlen(s) == 4); char s2[] = ""; assert(sizeof(s2) == 1); // + NULL assert(strlen(s2) == 0);
wchar_t s[] = L"我们 a"; assert(sizeof(s) == 16); // + NULL assert(wcslen(s) == 3);
可专门定义 UTF-16 和 UTF-32 编码字符串。
平台差异,wchar_t 宽度由编译器决定,故引入 char16_t、char32_t 类型。在 Linux 下,wchar_t 是 32 位整数。
标准库为 wchar_t 提供了和 char 几乎一致的字符和字符串函数。所以,用它处理 Unicode 最为便捷。
虽然 char32_t 和 wchar_t 基本一致,但更适合在指定 UTF 格式间转换,作为存储类型使用。
char32_t s[] = U"我们"; assert(sizeof(s) == 12); // 包括 NULL char32_t s[] = U""; assert(sizeof(s) == 4); // NULL
操作
以空白符分隔的相邻字面量会自动合并,还可以 \
换行。
char *s = "hello" "," "world!"; // 习惯写成指针样式。 puts(s);
char *s = "hello, \ world"; // 前面的空白视为内容组成部分。 puts(s);
用标准库函数,进行字符串编码转换。
int main (void) { setlocale(LC_ALL, "en_US.UTF-8"); char *s = "我们"; // UTF-8 to WCHAR wchar_t ws[4] = {0}; int n = swprintf(ws, 4, L"%s", s); assert(n == 2); assert(wcscmp(ws, L"我们") == 0); wprintf(L"%ls\n", ws); // WCHAR to UTF-8 char ss[10]= {0}; n = snprintf(ss, 10, "%ls", ws); assert(n == 6); assert(strcmp(ss, s) == 0); return 0; }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论