@@ -2207,7 +2207,7 @@ strrev(s.data()); // 会把按字符正常反转,得到 “岁万课开公师
2207
2207
2208
2208
# fun [直到 UTF-16 一夜之间成了丑陋的*变长编码* 。]
2209
2209
2210
- 闹了半天,Windows 费心费力替 Unicode 好不容易推广的 `wchar_t` ,既没有 UTF-8 兼容 ASCII 的好处,又没有 UTF-32 *定长编码* 的好处。可 “W 系” API 却又焊死在了 NT 内核最底层,反复来坑第一次用 Windows 编程的初学者。
2210
+ 闹了半天,Windows 费心费力替 Unicode 委员会好不容易推广的 `wchar_t` ,既没有 UTF-8 兼容 ASCII 的好处,又没有 UTF-32 *定长编码* 的好处。可 “W 系” API 却又焊死在了 NT 内核最底层,反复来坑第一次用 Windows 编程的初学者。
2211
2211
2212
2212
# fun [比尔盖子:你这样显得我很小丑诶?]
2213
2213
@@ -2280,10 +2280,10 @@ void thisFuncAcceptsUTF16(UTF16String msg);
2280
2280
- 方法:完全使用 `const char *` 和 `std::string` 。
2281
2281
- 代表作:Linux 文件系统 ext4、Lua 编程语言、现代 Python 中的 `bytes` 类型、HTTP 的 `?` 参数、早期 FAT32 文件系统等。
2282
2282
2283
- # detail [有人说 Linux 文件系统是 UTF-8?并不是!Linux 文件系统根本不会检验你的文件名是不是合法的 UTF-8,只不过是因为你设定了 `export LC_ALL=zh_CN.UTF-8` ,这会使所有程序(包括终端模拟器)假定文件名和文件内容都按 UTF-8 编码,从而调用操作系统各类 API 时(如 open、write)都会使用 UTF-8 编码的 `const char *` 输入,在 Linux 系统 API 看来,所谓“文件名”只是纯粹的字节流,只要保证不包含 `'/'` 和 `'\0'` ,无论你是什么编码,他都不在乎。而所有的 locale 都兼容 ASCII,所以绝不会出现一个中文汉字编码后产生 `'/'` 的情况(例如 GB2312 会把一个中文编码成两个 0x80 到 0xFF 区间的字节,和 ASCII 的范围没有重叠,更不可能出现 `'/'` ),即使换成 `export LC_ALL=zh_CN.GB2312` ,Linux 文件系统一样能正常工作,只不过读取你之前以 UTF-8 写入的文件会变成乱码而已。]
2284
-
2285
2283
这类软件是最常见的初学者写法,如果你从未想过字符编码问题,从不了解 `wchar_t` 、`char32_t` 之间的战争,只知道 `char` ,那么你已经自动在此阵营里。
2286
2284
2285
+ # detail [有人说 Linux 文件系统是 UTF-8?并不是!Linux 文件系统根本不会检验你的文件名是不是合法的 UTF-8,只不过是因为你设定了 `export LC_ALL=zh_CN.UTF-8` ,这会使所有程序(包括终端模拟器)假定文件名和文件内容都按 UTF-8 编码,从而调用操作系统各类 API 时(如 open、write)都会使用 UTF-8 编码的 `const char *` 输入,在 Linux 系统 API 看来,所谓“文件名”只是纯粹的字节流,只要保证不包含 `'/'` 和 `'\0'` ,无论你是什么编码,他都不在乎。而所有的 locale 都兼容 ASCII,所以绝不会出现一个中文汉字编码后产生 `'/'` 的情况(例如 GB2312 会把一个中文编码成两个 0x80 到 0xFF 区间的字节,和 ASCII 的范围没有重叠,更不可能出现 `'/'` ),即使换成 `export LC_ALL=zh_CN.GB2312` ,Linux 文件系统一样能正常工作,只不过读取你之前以 UTF-8 写入的文件会变成乱码而已。]
2286
+
2287
2287
对于中国区的 Windows 而言,他的所有 A 函数只支持 GBK 编码。这意味着如果你 Lua 中把字符串“当作” UTF-8 来用。那么当你在调用 Lua 的 io.open 前,需要先做一个 UTF-8 到 GBK 的转换,这还会导致丢失部分不在 GBK 内的字符,比如如果你的文件名包含 Emoji,那就会变成 `???` 乱码。而使用 W 函数的 UTF-16 就不会,因为 UTF-16 能容纳完整的 Unicode 映射。而完全摆烂的 Lua,其 `io.open` 只是使用 C 语言库函数 `fopen` ,`fopen` 又是基于 Windows 的 A 系列函数,Lua 又没有提供对 Windows C 运行时库特有的 `_wfopen` 函数的封装,从而永远不可能打开一个带有 Emoji 的文件。
2288
2288
2289
2289
*总结:要支持 ANSI 阵营,你什么都不需要做,char 满天飞摆烂。*
@@ -2488,7 +2488,7 @@ for (char32_t c : Utf8Range(s)) {
2488
2488
- 应用场景:通常认为,UTF-16 是纯粹的历史遗留糟粕,新软件不应该再使用 UTF-16。只有在和这些糟粕软件的 API 打交道时,才必须转换为 UTF-16。但也有人指出:UTF-16 是纯中文压缩率最高的编码格式,所以 UTF-16 还比较适合纯中文或以中文内容为主的文本数据压缩。
2489
2489
- 方法:始终以 UTF-16 编码存储和处理字符串。
2490
2490
- 优点:调用 Windows 系统 API 时无需任何转换,直接就能调用,最适合 Windows 本地开发,非跨平台。且对纯中文内容可比 UTF-8 额外节省 33% 空间。
2491
- - 缺点:对于 Windows 以外的系统就需要转换回 UTF-8,有少量性能开销。且如果存储的内容主要是纯英文,如 XML 代码等,内存占用会比 UTF-8 翻倍。而且 UTF-16 仍然是变长编码,虽然出现变长的概率较低,但不为 0,仍需要开发者做特殊处理。字符串的按码位反转会导致生僻字符出错,字符串以码点为单位的的正确切片、求长度等操作的复杂度仍然 $ O(N)$ 而不是通常的 $ O(1)$ 。
2491
+ - 缺点:对于 Windows 以外的系统就需要转换回 UTF-8,有少量性能开销。且如果存储的内容主要是纯英文,如 XML 代码等,内存占用会比 UTF-8 翻倍。而且 UTF-16 仍然是变长编码,虽然出现变长的概率较低,但不为 0,仍需要开发者做特殊处理。字符串的按码位反转会导致生僻字符出错,字符串以码点为单位的的正确切片、求长度等操作的复杂度仍然 $ O(N)$ 而不是通常的 $ O(1)$ 。并且 UTF-16 有大小端转换的问题。
2492
2492
- 代表作:Windows 系统 API、Java 语言、Windows 文件系统 (NTFS)、Qt、Word、JSON,他们都是 UTF-16 的受害者。
2493
2493
2494
2494
这相当于是把 UTF-16 当作了内码,但 UTF-16 依然是一种变长编码,对常见的中文处理没问题,生僻字就容易出问题,且因为出现概率低,很容易不发现,埋下隐患。
@@ -2633,11 +2633,9 @@ $ g++ -std=c++20 -lboost_locale main.cpp
2633
2633
2634
2634
```cmake
2635
2635
find_package(Boost REQUIRED COMPONENTS locale)
2636
- target_link_libraries(your_target Boost::locale)
2636
+ target_link_libraries(你的程序 Boost::locale)
2637
2637
```
2638
2638
2639
- # fun [如果你的项目要支持多语言,那么 Boost.Locale 就可以帮你做到。]
2640
-
2641
2639
==== GBK 和 UTF 互转
2642
2640
2643
2641
使用 `boost::locale::conv::to/from_utf` 就能轻易做到。
0 commit comments