开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 14天,点击查看活动详情
一、UTF-8 和 UTF-16
UTF-8和UTF-16是Unicode编码的两种不同实现方式。
- UTF-8:是一种变长字符编码方式,以8位为编码单元,用1到4个字节编码所有的Unicode字符,对于英文字符,只需要一个字节,而对于汉字等其他字符,则需要2-4个字节。由于其变长性质,UTF-8能够很好地支持国际化和多语言文字处理。
- UTF-16:是一种定长字符编码方式,以16位为编码单元,用2个字节编码大部分Unicode字符,而用4个字节编码一些较偏僻的字符。由于其定长性质,UTF-16 适合处理基于字符的算法,如搜索、替换等操作。
二、Unicode编码方式
Unicode 是一种字符编码方案,它用于把文本字符映射到数字编码。
2.1 浅析映射
以一个英文字符串 "Hello, World!"为例,我们要将这个字符串转换成 Unicode 编码。我们可以使用 Unicode 的一个子集,也就是 ASCII 编码。ASCII 编码只包含 128 个字符,其中包含了大部分常用的字符,比如字母、数字、标点符号等等。在 ASCII 编码中,每个字符都有一个数字表示,这个数字在 0 到 127 之间。
那么对于字符串 "Hello, World!" 来说,它就可以用下面的 ASCII 编码来表示:
72 101 108 108 111 44 32 87 111 114 108 100 33
这里的数字就是每个字符在 ASCII 编码中对应的数字。比如字符 H 在 ASCII 编码中的数字就是 72,字符 e 在 ASCII 编码中的数字就是 101,以此类推。
当我们使用 Unicode 编码时,我们需要为每个字符都分配一个唯一的数字。Unicode 编码用的是十六进制数,表示为 4 位或 6 位的数字。比如字符 L 的 ASCII 编码 108,对应的十六进制的 Unicode 编码就是 006C。
因此,我们可以将字符串 "Hello, World!" 转换成 Unicode 编码,得到下面的结果:
0048 0065 006C 006C 006F 002C 0020 0057 006F 0072 006C 0064 0021
附:ASCII编码表:
| 十进制 | 字符 | 十进制 | 字符 | 十进制 | 字符 | 十进制 | 字符 |
|---|---|---|---|---|---|---|---|
| 0 | NUL | 32 | ( | 64 | @ | 96 | ` |
| 1 | SOH | 33 | ) | 65 | A | 97 | a |
| 2 | STX | 34 | * | 66 | B | 98 | b |
| 3 | ETX | 35 | + | 67 | C | 99 | c |
| 4 | EOT | 36 | , | 68 | D | 100 | d |
| 5 | ENQ | 37 | - | 69 | E | 101 | e |
| 6 | ACK | 38 | . | 70 | F | 102 | f |
| 7 | BEL | 39 | / | 71 | G | 103 | g |
| 8 | BS | 40 | 0 | 72 | H | 104 | h |
| 9 | HT | 41 | 1 | 73 | I | 105 | i |
| 10 | LF | 42 | 2 | 74 | J | 106 | j |
| 11 | VT | 43 | 3 | 75 | K | 107 | k |
| 12 | FF | 44 | 4 | 76 | L | 108 | l |
| 13 | CR | 45 | 5 | 77 | M | 109 | m |
| 14 | SO | 46 | 6 | 78 | N | 110 | n |
| 15 | SI | 47 | 7 | 79 | O | 111 | o |
| 16 | DLE | 48 | 8 | 80 | P | 112 | p |
| 17 | DC1 | 49 | 9 | 81 | Q | 113 | q |
| 18 | DC2 | 50 | : | 82 | R | 114 | r |
| 19 | DC3 | 51 | ; | 83 | S | 115 | s |
| 20 | DC4 | 52 | < | 84 | T | 116 | t |
| 21 | NAK | 53 | = | 85 | U | 117 | u |
Unicode定义了几种编码方式,常用的有以下几种:
2.2 常见编码方式
1. UTF-8
UTF-8 是一种变长编码方式,它使用 1-4 个字节表示一个字符。ASCII 码使用 1 个字节,欧洲的其他字符使用 2-3 个字节,东亚字符使用 3 个字节。UTF-8 是目前最流行的 Unicode 编码方式之一,它适用于多种计算机操作系统和应用程序。
2. UTF-16
UTF-16 是一种定长编码方式,它使用 2 个字节或 4 个字节表示一个字符。UTF-16 主要用于 Windows 平台和 Java 程序中。UTF-16 可以表示 BMP(基本多文种平面)和非 BMP 字符。
3. UTF-32
UTF-32 是一种定长编码方式,它使用 4 个字节表示一个字符。UTF-32 可以表示 Unicode 中的任何字符,但由于它的字符编码比较大,因此它的存储空间相对较大。
4. UCS-2
UCS-2 是一种定长编码方式,它使用 2 个字节表示一个字符。它只能表示 BMP 中的字符,不能表示非 BMP 字符。
5. UCS-4
UCS-4 是一种定长编码方式,它使用 4 个字节表示一个字符。它可以表示 Unicode 中的任何字符,但由于它的字符编码比较大,因此它的存储空间相对较大。
总之,Unicode 编码方式主要区别在于它们的编码方式、字符集范围和存储空间。选择使用哪种编码方式应该根据实际需求来确定。
三、std::string 和 std::wstring
std::string 和 std::wstring 则是 C++ 标准库提供的两种不同的字符串类型:
- std::string:是由 char 类型构成的字符串类型,使用单字节字符集(如ASCII、GBK等)表示字符。在使用std::string 时需要注意字符集的兼容性,如在使用中文时可能需要转换字符集。
- std::wstring:是由wchar_t类型构成的字符串类型,使用双字节字符集(如UTF-16)表示字符。在处理多语言和国际化方面具有很好的兼容性,但是也会带来内存占用和处理效率方面的一些问题。对于C++11及以上的版本,建议使用 Unicode 字符串类型 std::wstring 和 std::wstringstream 等进行中文字符串的处理。其中 std::wstring 具体是何种 Unicode 类型,需要看你使用的操作系统和编译器,如果你使用的是 Windows 操作系统,且编译器采用的是 Visual C++ 编译器,那么
std::wstring字符串类型中的字符编码就是 UTF-16。如果你使用的是 Linux 或者 macOS 等操作系统,那么std::wstring字符串类型中的字符编码就是 UTF-32。
四、数据类型转换分析
举个例子,在Windows 操作系统,Visual C++ 编译器下,需要 将 SUStringRef 转 std::wstring 我们需要做什么呢?
- 首先明确 SketchUp API 中,SUStringRef 是指向 UTF-8 字符串的指针
- 在Windows 操作系统,Visual C++ 编译器下,std::wstring字符串类型中的字符编码就是 UTF-16
- 所以需要先将 SUStringRef 转换为 UTF-16 字符串
- 再将 UTF-16 字符串转换为 std::wstring
std::wstring SUStringRefToStdWstring(SUStringRef str) {
size_t len = 0;
SUStringGetUTF16Length(str, &len);
std::vector<wchar_t> buffer(len);
SUStringGetUTF16(str, len, &buffer[0], &len);
return std::wstring(buffer.begin(), buffer.end());
}
这里调用 SUStringGetUTF16Length 函数获取 SUStrignRef 类型字符串的长度,这里将长度存储在变量 len 中。创建一个长度为 len 的 wchar_t 类型的vector buffer用于存储转换后的 UTF-16 字符串。使用了 SUStringGetUTF16 函数将 SUStringRef 转换为 UTF-16 字符串,并使用了 std::vector 作为缓冲区来存储 UTF-16 字符串的内容。最后,使用 std::wstring 的构造函数将 UTF-16 字符串转换为 std::wstring。
4.1 补充:wchar_t 和 char
上面提到的 wchar_t 和 char 都是 C++ 中的字符类型,但是它们有一些区别。
char 类型是一个字节,常用于表示 ASCII 字符集中的字符,范围是 0 到 127 。
wchar_t 类型则通常占两个字节或四个字节,可以表示更多的字符集,例如Unicode字符集。在Windows平台上,wchar_t类型被广泛应用于API函数和字符串函数。
需要注意的是,使用 wchar_t 类型时,需要使用 wchar_t 类型的字符串字面量,例如 L"Hello, World!"。而不能使用普通的字符串字面量,例如 "Hello, World!",这会导致编译错误。
在实际开发中,如果需要处理 Unicode 字符集,应该使用 wchar_t 类型和 wchar_t 类型的字符串。如果只需要处理 ASCII 字符集,可以使用 char 类型和普通字符串。