Skip to content

Commit 020a59b

Browse files
committed
fixallmath
1 parent 2f96cd5 commit 020a59b

File tree

3 files changed

+19
-14
lines changed

3 files changed

+19
-14
lines changed

docs/design.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@
77
## 动态类型的缺点
88

99
$$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$
10+
11+
你好 $O(N)$ 啊!

docs/unicode.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ std::string s = u8"你好";
965965
- 应用场景:常见于文字处理需求不大,但有强烈的跨平台需求,特别是互联网方面的软件。他们通常只用到字符串的拼接、查找、切片通常也只是在固定的位置(例如文件分隔符 `'/'`)。也非常适合主要面对的是以 ASCII 为主的“代码”类文本,UTF-8 是对英文类文本压缩率最高的,所以也广泛用于编译器、数据库之类的场景。同时因为 UTF-8 完全兼容 ASCII,使得他能轻易适配远古的 C 语言程序和库。
966966
- 方法:始终以 UTF-8 编码存储和处理字符串。
967967
- 优点:跨平台,在网络传输时无需任何转码,UTF-8 是互联网的主流编码格式,不同平台上运行的 UTF-8 软件可以随意共享文本数据。兼容 ASCII,方便复用现有库和生态。对英文类文本压缩率高,对中文文本也不算太差。
968-
- 缺点:对于底层 API 均采用 UTF-16 的 Windows 系统,需要进行字符编码转换,有少量性能损失。且字符串的正确切片、求长度等操作的复杂度会变成 $$$O(N)$$$ 而不是通常的 $$O(1)$$
968+
- 缺点:对于底层 API 均采用 UTF-16 的 Windows 系统,需要进行字符编码转换,有少量性能损失。且字符串的正确切片、求长度等操作的复杂度会变成 $O(N)$ 而不是通常的 $O(1)$。
969969
- 代表作:Rust 语言、Go 语言、CMake 构建系统、Julia 语言等。
970970

971971
在 C++ 中,可以通过 `u8"你好"` 创建一个保证内部是 UTF-8 编码的字符串常量,类型为 `char8_t []`
@@ -1053,7 +1053,7 @@ Rust 和 Go:严格区分“字符 (32 位)”和“字节 (8 位)”的概念
10531053
|Go|`rune`|`byte`|
10541054
|Julia|`Char`|`UInt8`|
10551055

1056-
为此,这些语言都为字符串提供了两套 API,一种是按字符索引,一种是按字节索引。按字符索引时,会从头开始,逐个解析码位,直到解析到想要的字符为止,复杂度 $$$O(N)$$$。按字节索引时,直接跳到指定字节,无需解析,复杂度 $$O(1)$$
1056+
为此,这些语言都为字符串提供了两套 API,一种是按字符索引,一种是按字节索引。按字符索引时,会从头开始,逐个解析码位,直到解析到想要的字符为止,复杂度 $O(N)$。按字节索引时,直接跳到指定字节,无需解析,复杂度 $O(1)$。
10571057

10581058
```rust
10591059
let s = "你好";
@@ -1154,7 +1154,7 @@ for (char32_t c : Utf8Range(s)) {
11541154
- 应用场景:通常认为,UTF-16 是纯粹的历史遗留糟粕,新软件不应该再使用 UTF-16。只有在和这些糟粕软件的 API 打交道时,才必须转换为 UTF-16。但也有人指出:UTF-16 是纯中文压缩率最高的编码格式,所以 UTF-16 还比较适合纯中文或以中文内容为主的文本数据压缩。
11551155
- 方法:始终以 UTF-16 编码存储和处理字符串。
11561156
- 优点:调用 Windows 系统 API 时无需任何转换,直接就能调用,最适合 Windows 本地开发,非跨平台。且对纯中文内容可比 UTF-8 额外节省 33% 空间。
1157-
- 缺点:对于 Windows 以外的系统就需要转换回 UTF-8,有少量性能开销。且如果存储的内容主要是纯英文,如 XML 代码等,内存占用会比 UTF-8 翻倍。而且 UTF-16 仍然是变长编码,虽然出现变长的概率较低,但不为 0,仍需要开发者做特殊处理。字符串的按码位反转会导致生僻字符出错,字符串以码点为单位的的正确切片、求长度等操作的复杂度仍然 $$$O(N)$$$ 而不是通常的 $$O(1)$$。并且 UTF-16 有大小端转换的问题。
1157+
- 缺点:对于 Windows 以外的系统就需要转换回 UTF-8,有少量性能开销。且如果存储的内容主要是纯英文,如 XML 代码等,内存占用会比 UTF-8 翻倍。而且 UTF-16 仍然是变长编码,虽然出现变长的概率较低,但不为 0,仍需要开发者做特殊处理。字符串的按码位反转会导致生僻字符出错,字符串以码点为单位的的正确切片、求长度等操作的复杂度仍然 $O(N)$ 而不是通常的 $O(1)$。并且 UTF-16 有大小端转换的问题。
11581158
- 代表作:Windows 系统 API、Java 语言、Windows 文件系统 (NTFS)、Qt、Word、JSON,他们都是 UTF-16 的受害者。
11591159
11601160
这相当于是把 UTF-16 当作了内码,但 UTF-16 依然是一种变长编码,对常见的中文处理没问题,生僻字就容易出问题,且因为出现概率低,很容易不发现,埋下隐患。
@@ -1217,7 +1217,7 @@ fmt::println("{}", s.size()); // 3
12171217
12181218
- 应用场景:适合需要经常处理文字的领域,如文本编辑器、浏览器等。但不适合存储和传输,因为浪费硬盘和网络带宽。字符串一般都长期以 UTF-8 存储,只有在需要频繁索引码位时,才需要转换为 UTF-32。
12191219
- 方法:始终以 UTF-32 编码存储和处理字符串。
1220-
- 优点:字符串的按码位反转、切片、求长度等操作都是 $$$O(1)$$$ 的复杂度,可以当作普通数组一样,随意处理。例如你可以设想一个文本编辑框,需要支持“退格”操作,如果是 UTF-8 和 UTF-16 就需要繁琐的判断代理对、各种车厢,而 UTF-32 的字符串只需要一次 `pop_back` 就搞定了。
1220+
- 优点:字符串的按码位反转、切片、求长度等操作都是 $O(1)$ 的复杂度,可以当作普通数组一样,随意处理。例如你可以设想一个文本编辑框,需要支持“退格”操作,如果是 UTF-8 和 UTF-16 就需要繁琐的判断代理对、各种车厢,而 UTF-32 的字符串只需要一次 `pop_back` 就搞定了。
12211221
- 缺点:浪费空间大,通常在保存时,仍然需要转换回 UTF-8 后再写入文件,有一定性能开销。
12221222
12231223
*总结:要支持 UTF-32 阵营,请全部使用 `char32_t` 和 `std::u32string`。字面量全用 `U"你好"` 的形式书写,读文件时转为 UTF-32,写文件时转回 UTF-8。*
@@ -1827,7 +1827,7 @@ TODO
18271827
//
18281828
//缺点是这样的软件会无法跨平台,因为 `wchar_t` 在 Linux 上是安全的内码 UTF-32。而 Windows 上是 UTF-16,是不定长的编码,如果存在“𰻞”和“😉”这样超过 0x10000 的生僻字,就会产生两个 `wchar_t`!如果文字处理涉及切片,就会出问题。概率很低,但不为零,软件仍然需要对可能存在的双 `wchar_t` 做特殊处理。若不处理,轻则乱码,重则留下漏洞,被黑客攻击,加重了 Windows 和 Java 程序员的心智负担。
18291829
//
1830-
//如果一个程序(例如 GCC)只适配了 `wchar_t` 是 UTF-32 的平台,想当然的把 `wchar_t` 当作安全的定长内码使用,那移植到 Windows 上后就会丧失处理“𰻞”和“😉”的能力。要么就需要对所有代码大改,把原本 $$$O(1)$$$ 的字符串求长度改成 $$O(N)$$ 的;要么出现乱码,被黑客攻击。
1830+
//如果一个程序(例如 GCC)只适配了 `wchar_t` 是 UTF-32 的平台,想当然的把 `wchar_t` 当作安全的定长内码使用,那移植到 Windows 上后就会丧失处理“𰻞”和“😉”的能力。要么就需要对所有代码大改,把原本 $O(1)$ 的字符串求长度改成 $O(N)$ 的;要么出现乱码,被黑客攻击。
18311831
//
18321832
//当需要读写二进制文件时,使用 `fstream`,原封不动地按“字节”为单位读取。
18331833
//
@@ -2297,7 +2297,7 @@ stream << "你好,世界\n"; // 写入 QString,QTextStream 会自动将其
22972297
22982298
如果用 UTF-8 或 UTF-16 来存储的话,会遇到变长编码的固有缺陷:
22992299
2300-
例如像字符串索引,字符串求长度等操作,要么索引出来的是字节而不是字符了;要么就需要 $$$O(N)$$$ 的复杂度,逐一遍历每个字节,才能确定真正的位置;哪怕全是 ASCII 也得这么做,因为万一刚好有一个是中文字符呢?
2300+
例如像字符串索引,字符串求长度等操作,要么索引出来的是字节而不是字符了;要么就需要 $O(N)$ 的复杂度,逐一遍历每个字节,才能确定真正的位置;哪怕全是 ASCII 也得这么做,因为万一刚好有一个是中文字符呢?
23012301
23022302
所以,对于经常需要处理字符串的 Python 来说,UTF-8 是无法接受的,似乎只能以 UTF-32 来存储?
23032303
@@ -2344,7 +2344,7 @@ struct PyUnicodeString {
23442344

23452345
### Rust `&str``String`
23462346

2347-
而 Rust 则采用了字符串全员 UTF-8 的策略,这是因为 Rust 最常用于互联网方面的底层系统软件,互联网最常用的文本编码就是 UTF-8,没有大小端问题,且国际通用。除此之外,互联网基建最常见的平台就是 Linux,使用 UTF-8 存储字符串,调用 Linux 系统 API 无需任何转换。且文本文件基本都可以假定是 UTF-8 编码,写入时无需任何转换,复杂度低至 $$$O(1)$$$。作为代价,这导致文本处理上的一些困难,例如字符串的索引,需要区分是按字节索引还是按字符索引,如果确实需要按字符索引的话,复杂度就会是 $$O(N)$$ 了。
2347+
而 Rust 则采用了字符串全员 UTF-8 的策略,这是因为 Rust 最常用于互联网方面的底层系统软件,互联网最常用的文本编码就是 UTF-8,没有大小端问题,且国际通用。除此之外,互联网基建最常见的平台就是 Linux,使用 UTF-8 存储字符串,调用 Linux 系统 API 无需任何转换。且文本文件基本都可以假定是 UTF-8 编码,写入时无需任何转换,复杂度低至 $O(1)$。作为代价,这导致文本处理上的一些困难,例如字符串的索引,需要区分是按字节索引还是按字符索引,如果确实需要按字符索引的话,复杂度就会是 $O(N)$ 了。
23482348

23492349
无论如何,如果你选择了 UTF-8 流派的话,Rust 字符串的“迭代器双轨制”确实值得称道:
23502350

mkdocs.yml

+10-7
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ theme:
2929
- search.suggest
3030
- search.highlight
3131
extra_javascript:
32-
- extra.js
33-
- https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML
32+
- extra.js
33+
- https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML
3434
extra_css:
35-
- extra.css
35+
- extra.css
3636
markdown_extensions:
37-
- mdx_math
38-
- toc
39-
- sane_lists
40-
- smarty
37+
- extra
38+
- tables
39+
- toc
40+
- fenced_code
41+
- sane_lists
42+
- smarty
43+
- pymdownx.arithmatex

0 commit comments

Comments
 (0)