Skip to content

Commit a7a733e

Browse files
committed
update binaries chapter
1 parent 79ae771 commit a7a733e

File tree

1 file changed

+98
-70
lines changed

1 file changed

+98
-70
lines changed

6-bin-str-charlist.md

+98-70
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
1-
6-二进制-字符串-字符列表
1+
6-二进制串、字符串和字符列表
22
========================
3-
[UTF-8和Unicode](#61-utf-8%E5%92%8Cunicode)
4-
[二进制(和bitstring)](#62-%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%92%8Cbitstring)
5-
[字符列表](#63-%E5%AD%97%E7%AC%A6%E5%88%97%E8%A1%A8)
63

7-
在“基本类型”一章中,介绍了字符串,以及使用```is_binary/1```函数检查它:
4+
在“基本类型”一章中,介绍了字符串,以及使用`is_binary/1`函数检查它:
5+
86
```elixir
97
iex> string = "hello"
108
"hello"
119
iex> is_binary string
1210
true
1311
```
1412

15-
本章将学习理解,二进制(binaries)是个啥,它怎么和字符串扯上关系的。
16-
以及用单引号包裹的值,```'like this'```是啥意思。
17-
18-
## 6.1-UTF-8和Unicode
19-
字符串是UTF-8编码的二进制。
20-
为了弄清这句话啥意思,我们要先理解两个概念:bytes和code point的区别。
21-
字母```a```的code point是97,而字母```ł```的code point是322。
22-
当把字符串```"hełło"```写到硬盘上的时候,需要将其code point转化为bytes。
23-
如果一个byte对应一个code point,那是写不了```"hełło"```的,
24-
因为字母```ł```的code point是322,超过了一个byte所能存储的最大数值(255)。
25-
但是如你所见,该字母能够显示到屏幕上,说明还是有一定的解决方法的。于是 _编码_ 便出现了。
26-
27-
要用byte表示code point,我们需要在一定程度上对其进行编码。
28-
Elixir使用UTF-8为默认编码格式。
29-
当我们说某个字符串是UTF-8编码的二进制数据,意思是该字符串是一串byte,
30-
以一定方法组织来表示特定的code points,即UTF-8编码。
31-
32-
因此当我们存储字母```ł```的时候,实际上是用两个bytes来表示它。
33-
这就是为什么有时候对同一字符串,调用函数```byte_size/1``````String.length/1```
34-
结果不一样:
13+
本章将学习理解:二进制串(binaries)是个啥,它怎么和字符串(strings)扯上关系的;
14+
以及用单引号包裹的值`'like this'`是啥意思。
15+
16+
## UTF-8和Unicode
17+
18+
字符串是UTF-8编码的二进制串。
19+
为了弄清这句话的准确含义,我们要先理解两个概念:字节(bytes)和字符编码(code point)的区别。
20+
Unicode标准为我们已知的大部分字母分配了字符编码。
21+
比如,字母`a`的字符编码是`97`,而字母`ł`的字符编码是`322`
22+
当把字符串`"hełło"`写到硬盘上的时候,需要将字符编码转化为字节。
23+
如果我们遵循一个字节表示一个字符编码这个,那是写不了`"hełło"`的。
24+
因为字母`ł`的编码是`322`,而一个字节所能存储的数值范围是`0``255`
25+
但是如你所见,确实能够在屏幕上显示`"hełło"`,说明还是有*某种*解决方法的,于是*编码*便出现了。
26+
27+
要用字节表示字符编码,我们需要用某种方式对其进行编码。
28+
Elixir选择UTF-8为主要并且默认的编码方式。
29+
当我们说某个字符串是UTF-8编码的二进制串,指的是该字符串是一串字节,
30+
这些字节以某种方式(即UTF-8编码)组织起来,表示特定的字符编码。
31+
32+
因为给字母`ł`分配的字符编码是`322`,因此在实际上需要一个以上的字节来表示。
33+
这就是为什么我们会看到,调用函数`byte_size/1``String.length/1`的结果不一样:
34+
3535
```elixir
3636
iex> string = "hełło"
3737
"hełło"
@@ -41,8 +41,12 @@ iex> String.length string
4141
5
4242
```
4343

44-
UTF-8需要1个byte来表示code points:h,e和o,用2个bytes表示ł。
45-
在Elixir中可以使用```?```运算符获取code point值:
44+
>注意:如果你使用Windows,你的终端有可能不是默认使用UTF-8编码方式。你需要在进入`iex(iex.bat)`之前,
45+
首先执行`chcp 65001`命令来修改当前Session的编码方式。
46+
47+
UTF-8需要1个字节来表示`h``e``o`的字符编码,用2个字节表示`ł`
48+
在Elixir中可以使用`?`运算符获取字符的编码:
49+
4650
```elixir
4751
iex> ?a
4852
97
@@ -51,82 +55,92 @@ iex> ?ł
5155
```
5256

5357
你还可以使用
54-
[String模块](http://elixir-lang.org/docs/stable/elixir/String.html)里的函数
55-
将字符串切成单独的code points:
58+
[String](http://elixir-lang.org/docs/stable/elixir/String.html)模块里的函数
59+
将字符串切成单独的字符编码:
60+
5661
```elixir
5762
iex> String.codepoints("hełło")
5863
["h", "e", "ł", "ł", "o"]
5964
```
6065

61-
Elixir为字符串操作提供了强大的支持。实际上,Elixir通过了文章
62-
[字符串类型破了](http://mortoray.com/2013/11/27/the-string-type-is-broken/)
66+
Elixir为字符串操作提供了强大的支持,它支持Unicode的许多操作。实际上,Elixir通过了文章
67+
[字符串类型崩坏了](http://mortoray.com/2013/11/27/the-string-type-is-broken/)
6368
记录的所有测试。
6469

65-
不仅如此,因为字符串是二进制,Elixir还提供了更强大的底层类型的操作。
66-
下面就来介绍该底层类型---二进制。
70+
然而,字符串只是故事的一小部分。如果字符串正如所言是二进制串,那我们使用`is_binaries/1`函数时,
71+
Elixir必须一个底层类型来支持字符串。事实亦如此,下面就来介绍这个底层类型---二进制串。
72+
73+
## 二进制串(以及比特串`bitstring`
74+
75+
在Elixir中可以用`<<>>`定义一个二进制串:
6776

68-
## 6.2-二进制(和bitstring)
69-
在Elixir中可以用```<<>>```定义一个二进制:
7077
```elixir
7178
iex> <<0, 1, 2, 3>>
7279
<<0, 1, 2, 3>>
73-
iex> byte_size <<0, 1, 2, 3>>
80+
iex> byte_size(<<0, 1, 2, 3>>)
7481
4
7582
```
7683

77-
一个二进制只是一连串bytes。这些bytes可以以任何方法组织,即使凑不成一个合法的字符串:
84+
一个二进制串只是一连串的字节而已。这些字节可以以任何方式组织,即使凑不成一个合法的字符串:
85+
7886
```elixir
7987
iex> String.valid?(<<239, 191, 191>>)
8088
false
8189
```
8290

83-
字符串的拼接操作实际上是二进制的拼接操作:
91+
而字符串的拼接操作实际上就是二进制串的拼接操作:
92+
8493
```elixir
8594
iex> <<0, 1>> <> <<2, 3>>
8695
<<0, 1, 2, 3>>
8796
```
8897

89-
一个常见技巧是,通过给某字符串尾部拼接一个null byte```<<0>>```
90-
来看看该字符串内部二进制的样子:
98+
一个常见技巧是,通过给一个字符串尾部拼接一个空(null)字节`<<0>>`
99+
可以看到该字符串内部二进制串的样子:
100+
91101
```elixir
92102
iex> "hełło" <> <<0>>
93103
<<104, 101, 197, 130, 197, 130, 111, 0>>
94104
```
95105

96-
二进制中的每个数值都表示一个byte,因此其最大是255。
97-
如果超出了255,二进制允许你再提供一个修改器(标识一下那个位置的存储空间大小)使其可以存储;
98-
或者将其转换为utf8编码后的形式(变成多个byte的二进制):
106+
二进制串中的每个数值都表示一个字节,其数值最大范围是255。
107+
二进制允许使用修改器显式标注一下那个数值的存储空间大小,使其可以存储超过255的数值;
108+
或者将一个字符编码转换为utf8编码后的形式(变成多个字节的二进制串):
109+
99110
```elixir
100111
iex> <<255>>
101112
<<255>>
102-
iex> <<256>> # truncated
113+
iex> <<256>> # 被截断(truncated
103114
<<0>>
104-
iex> <<256 :: size(16)>> # use 16 bits (2 bytes) to store the number
115+
iex> <<256 :: size(16)>> # 使用16比特(bits)即2个字节来保存
105116
<<1, 0>>
106-
iex> <<256 :: utf8>> # the number is a code point
107-
"Ā"
108-
iex> <<256 :: utf8, 0>>
117+
iex> <<256 :: utf8>> # 这个数字是一个字符的编码,将其使用utf8方式编码为字节
118+
"Ā" # 注意,在iex交互窗口中,所有可以作为合法字符串的二进制串,都会显示为字符串
119+
iex> <<256 :: utf8, 0>> # 尾部拼接个空字节,查看上一条命令结果内部实际的二进制串
109120
<<196, 128, 0>>
110121
```
111122

112-
如果一个byte是8 bits,那如果我们给一个size是1 bit的修改器会怎样?:
123+
如果一个字节是8个比特,那如果我们给一个大小是1比特的修改器会怎样?:
124+
113125
```elixir
114126
iex> <<1 :: size(1)>>
115127
<<1::size(1)>>
116-
iex> <<2 :: size(1)>> # truncated
128+
iex> <<2 :: size(1)>> # 被截断(truncated
117129
<<0::size(1)>>
118-
iex> is_binary(<< 1 :: size(1)>>)
130+
iex> is_binary(<< 1 :: size(1)>>) # 二进制串失格
119131
false
120132
iex> is_bitstring(<< 1 :: size(1)>>)
121133
true
122134
iex> bit_size(<< 1 :: size(1)>>)
123135
1
124136
```
125-
这样(每个元素是1 bit)就不再是二进制(人家每个元素是byte,至少8 bits)了,
126-
而是bitstring,就是一串比特!
127-
所以实际上二进制就是一串比特,只是比特数是8的倍数。
128137

129-
也可以对二进制或bitstring做模式匹配:
138+
这样(每个元素长度是1比特)就不再是二进制串(人家每个元素是一个字节,起码8比特),
139+
退化成为比特串(bitstring),意思就是一串比特!
140+
所以,所以,二进制串就是一特殊的比特串,比特总数是8的倍数。
141+
142+
也可以对二进制串或比特串做模式匹配:
143+
130144
```elixir
131145
iex> <<0, 1, x>> = <<0, 1, 2>>
132146
<<0, 1, 2>>
@@ -136,32 +150,43 @@ iex> <<0, 1, x>> = <<0, 1, 2, 3>>
136150
** (MatchError) no match of right hand side value: <<0, 1, 2, 3>>
137151
```
138152

139-
注意(没有修改器标识的情况下)二进制中的每个元素都应该匹配8 bits
140-
因此上面最后的例子,匹配的左右两端不具有相同容量,因此出现错误。
153+
注意,在没有修改器标识的情况下,二进制串中的每个元素都应该匹配8个比特长度
154+
因此上面最后的例子,匹配的左右两端不具有相同容量,因此出现错误。
141155

142156
下面是使用了修改器标识的匹配例子:
157+
143158
```elixir
144159
iex> <<0, 1, x :: binary>> = <<0, 1, 2, 3>>
145160
<<0, 1, 2, 3>>
146161
iex> x
147162
<<2, 3>>
148163
```
149-
上面的模式仅在二进制 **尾部** 元素被修改器标识为又一个二进制时才正确。
150-
字符串的连接操作也是一个意思:
164+
165+
上面例子使用了`binary`修改器,指示`x`是个二进制串。(为啥不用单词的复数形式`binaries`搞不懂啊。)
166+
这个修改器仅仅可以用在被匹配的串的*末尾元素*上。
167+
168+
跟上面例子同样的原理,使用字符串的连接操作符`<>`,效果相似:
169+
151170
```elixir
152171
iex> "he" <> rest = "hello"
153172
"hello"
154173
iex> rest
155174
"llo"
156175
```
157176

158-
总之,记住字符串是UTF-8编码的二进制,而二进制是特殊的、数量是8的倍数的bitstring。
159-
这种机制增加了Elixir在处理bits或bytes时的灵活性。
160-
而现实中99%的时候你会用```is_binary/1``````byte_size/1```函数跟二进制打交道。
177+
A complete reference about the binary / bitstring constructor <<>> can be found in the Elixir documentation. This concludes our tour of bitstrings, binaries and strings. A string is a UTF-8 encoded binary and a binary is a bitstring where the number of bits is divisible by 8. Although this shows the flexibility Elixir provides for working with bits and bytes, 99% of the time you will be working with binaries and using the is_binary/1 and byte_size/1 functions.
178+
179+
关于二进制串/比特串的构造器`<< >>`完整的参考,
180+
请见[Elixir的文档](http://elixir-lang.org/docs/stable/elixir/Kernel.SpecialForms.html#%3C%3C%3E%3E/1)
181+
182+
总之,记住字符串是UTF-8编码后的二进制串,而二进制串是特殊的、元素数量是8的倍数的比特串。
183+
尽管这种机制增加了Elixir在处理比特或字节时的灵活性,
184+
而现实中99%的时候你只会用到`is_binary/1``byte_size/1`函数跟二进制串打交道。
185+
186+
## 字符列表(char lists)
161187

162-
## 6.3-字符列表
163188
字符列表就是字符的列表。
164-
双引号包裹字符串,单引号包裹字符列表。
189+
165190
```elixir
166191
iex> 'hełło'
167192
[104, 101, 322, 322, 111]
@@ -170,12 +195,14 @@ true
170195
iex> 'hello'
171196
'hello'
172197
```
173-
字符列表存储的不是bytes,而是字符的code points(实际上就是这些code points的普通列表)。
174-
如果某字符不属于ASCII返回,iex就打印它的code point。
175198

176-
实际应用中,字符列表常被用来做参数,同一些老的库,或者同Erlang平台交互。
177-
因为这些老库不接受二进制作为参数。
178-
将字符列表和字符串之间转换,使用函数```to_string/1``````to_char_list/1```
199+
可以看出,比起包含字节,一个字符列表包含的是单引号所引用的一串字符各自的字符编码。
200+
注意IEx遇到超出ASCII值范围的字符编码时,显示其字符编码的值,而不是字符。
201+
双引号引用的是字符串(即二进制串),单引号表示的是字符列表(即,一个列表)。
202+
203+
实际应用中,字符列表常被用来做为同一些Erlang库交互的参数,因为这些老库不接受二进制串作为参数。
204+
要将字符列表和字符串之间相互转换,可以使用函数`to_string/1``to_char_list/1`
205+
179206
```elixir
180207
iex> to_char_list "hełło"
181208
[104, 101, 322, 322, 111]
@@ -186,4 +213,5 @@ iex> to_string :hello
186213
iex> to_string 1
187214
"1"
188215
```
189-
注意这些函数是多态的。它们不但转化字符列表和字符串,还能转化字符串和整数,等等。
216+
217+
注意这些函数是多态的。它们不但可以将字符列表转化为字符串,还能转化整数、原子等为字符串。

0 commit comments

Comments
 (0)