1
- 6-二进制-字符串-字符列表
1
+ 6-二进制串、字符串和字符列表
2
2
========================
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 )
6
3
7
- 在“基本类型”一章中,介绍了字符串,以及使用``` is_binary/1 ``` 函数检查它:
4
+ 在“基本类型”一章中,介绍了字符串,以及使用` is_binary/1 ` 函数检查它:
5
+
8
6
``` elixir
9
7
iex> string = " hello"
10
8
" hello"
11
9
iex> is_binary string
12
10
true
13
11
```
14
12
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
+
35
35
``` elixir
36
36
iex> string = " hełło"
37
37
" hełło"
@@ -41,8 +41,12 @@ iex> String.length string
41
41
5
42
42
```
43
43
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
+
46
50
``` elixir
47
51
iex> ?a
48
52
97
@@ -51,82 +55,92 @@ iex> ?ł
51
55
```
52
56
53
57
你还可以使用
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
+
56
61
``` elixir
57
62
iex> String .codepoints (" hełło" )
58
63
[" h" , " e" , " ł" , " ł" , " o" ]
59
64
```
60
65
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/ )
63
68
记录的所有测试。
64
69
65
- 不仅如此,因为字符串是二进制,Elixir还提供了更强大的底层类型的操作。
66
- 下面就来介绍该底层类型---二进制。
70
+ 然而,字符串只是故事的一小部分。如果字符串正如所言是二进制串,那我们使用` is_binaries/1 ` 函数时,
71
+ Elixir必须一个底层类型来支持字符串。事实亦如此,下面就来介绍这个底层类型---二进制串。
72
+
73
+ ## 二进制串(以及比特串` bitstring ` )
74
+
75
+ 在Elixir中可以用` <<>> ` 定义一个二进制串:
67
76
68
- ## 6.2-二进制(和bitstring)
69
- 在Elixir中可以用``` <<>> ``` 定义一个二进制:
70
77
``` elixir
71
78
iex> << 0 , 1 , 2 , 3 >>
72
79
<< 0 , 1 , 2 , 3 >>
73
- iex> byte_size << 0 , 1 , 2 , 3 >>
80
+ iex> byte_size ( << 0 , 1 , 2 , 3 >> )
74
81
4
75
82
```
76
83
77
- 一个二进制只是一连串bytes。这些bytes可以以任何方法组织,即使凑不成一个合法的字符串:
84
+ 一个二进制串只是一连串的字节而已。这些字节可以以任何方式组织,即使凑不成一个合法的字符串:
85
+
78
86
``` elixir
79
87
iex> String .valid? (<< 239 , 191 , 191 >> )
80
88
false
81
89
```
82
90
83
- 字符串的拼接操作实际上是二进制的拼接操作:
91
+ 而字符串的拼接操作实际上就是二进制串的拼接操作:
92
+
84
93
``` elixir
85
94
iex> << 0 , 1 >> <> << 2 , 3 >>
86
95
<< 0 , 1 , 2 , 3 >>
87
96
```
88
97
89
- 一个常见技巧是,通过给某字符串尾部拼接一个null byte``` <<0>> ``` ,
90
- 来看看该字符串内部二进制的样子:
98
+ 一个常见技巧是,通过给一个字符串尾部拼接一个空(null)字节` <<0>> ` ,
99
+ 可以看到该字符串内部二进制串的样子:
100
+
91
101
``` elixir
92
102
iex> " hełło" <> << 0 >>
93
103
<< 104 , 101 , 197 , 130 , 197 , 130 , 111 , 0 >>
94
104
```
95
105
96
- 二进制中的每个数值都表示一个byte,因此其最大是255。
97
- 如果超出了255,二进制允许你再提供一个修改器(标识一下那个位置的存储空间大小)使其可以存储;
98
- 或者将其转换为utf8编码后的形式(变成多个byte的二进制):
106
+ 二进制串中的每个数值都表示一个字节,其数值最大范围是255。
107
+ 二进制允许使用修改器显式标注一下那个数值的存储空间大小,使其可以存储超过255的数值;
108
+ 或者将一个字符编码转换为utf8编码后的形式(变成多个字节的二进制串):
109
+
99
110
``` elixir
100
111
iex> << 255 >>
101
112
<< 255 >>
102
- iex> << 256 >> # truncated
113
+ iex> << 256 >> # 被截断( truncated)
103
114
<< 0 >>
104
- iex> << 256 :: size (16 )>> # use 16 bits (2 bytes) to store the number
115
+ iex> << 256 :: size (16 )>> # 使用16比特( bits)即2个字节来保存
105
116
<< 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 >> # 尾部拼接个空字节,查看上一条命令结果内部实际的二进制串
109
120
<< 196 , 128 , 0 >>
110
121
```
111
122
112
- 如果一个byte是8 bits,那如果我们给一个size是1 bit的修改器会怎样?:
123
+ 如果一个字节是8个比特,那如果我们给一个大小是1比特的修改器会怎样?:
124
+
113
125
``` elixir
114
126
iex> << 1 :: size (1 )>>
115
127
<< 1 :: size (1 )>>
116
- iex> << 2 :: size (1 )>> # truncated
128
+ iex> << 2 :: size (1 )>> # 被截断( truncated)
117
129
<< 0 :: size (1 )>>
118
- iex> is_binary (<< 1 :: size (1 )>> )
130
+ iex> is_binary (<< 1 :: size (1 )>> ) # 二进制串失格
119
131
false
120
132
iex> is_bitstring (<< 1 :: size (1 )>> )
121
133
true
122
134
iex> bit_size (<< 1 :: size (1 )>> )
123
135
1
124
136
```
125
- 这样(每个元素是1 bit)就不再是二进制(人家每个元素是byte,至少8 bits)了,
126
- 而是bitstring,就是一串比特!
127
- 所以实际上二进制就是一串比特,只是比特数是8的倍数。
128
137
129
- 也可以对二进制或bitstring做模式匹配:
138
+ 这样(每个元素长度是1比特)就不再是二进制串(人家每个元素是一个字节,起码8比特),
139
+ 退化成为比特串(bitstring),意思就是一串比特!
140
+ 所以,所以,二进制串就是一特殊的比特串,比特总数是8的倍数。
141
+
142
+ 也可以对二进制串或比特串做模式匹配:
143
+
130
144
``` elixir
131
145
iex> << 0 , 1 , x>> = << 0 , 1 , 2 >>
132
146
<< 0 , 1 , 2 >>
@@ -136,32 +150,43 @@ iex> <<0, 1, x>> = <<0, 1, 2, 3>>
136
150
** (MatchError ) no match of right hand side value: << 0 , 1 , 2 , 3 >>
137
151
```
138
152
139
- 注意(没有修改器标识的情况下)二进制中的每个元素都应该匹配8 bits 。
140
- 因此上面最后的例子,匹配的左右两端不具有相同容量,因此出现错误。
153
+ 注意,在没有修改器标识的情况下,二进制串中的每个元素都应该匹配8个比特长度 。
154
+ 因此上面最后的例子,匹配的左右两端不具有相同容量,因此出现错误。
141
155
142
156
下面是使用了修改器标识的匹配例子:
157
+
143
158
``` elixir
144
159
iex> << 0 , 1 , x :: binary>> = << 0 , 1 , 2 , 3 >>
145
160
<< 0 , 1 , 2 , 3 >>
146
161
iex> x
147
162
<< 2 , 3 >>
148
163
```
149
- 上面的模式仅在二进制 ** 尾部** 元素被修改器标识为又一个二进制时才正确。
150
- 字符串的连接操作也是一个意思:
164
+
165
+ 上面例子使用了` binary ` 修改器,指示` x ` 是个二进制串。(为啥不用单词的复数形式` binaries ` 搞不懂啊。)
166
+ 这个修改器仅仅可以用在被匹配的串的* 末尾元素* 上。
167
+
168
+ 跟上面例子同样的原理,使用字符串的连接操作符` <> ` ,效果相似:
169
+
151
170
``` elixir
152
171
iex> " he" <> rest = " hello"
153
172
" hello"
154
173
iex> rest
155
174
" llo"
156
175
```
157
176
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)
161
187
162
- ## 6.3-字符列表
163
188
字符列表就是字符的列表。
164
- 双引号包裹字符串,单引号包裹字符列表。
189
+
165
190
``` elixir
166
191
iex> ' hełło'
167
192
[104 , 101 , 322 , 322 , 111 ]
@@ -170,12 +195,14 @@ true
170
195
iex> ' hello'
171
196
' hello'
172
197
```
173
- 字符列表存储的不是bytes,而是字符的code points(实际上就是这些code points的普通列表)。
174
- 如果某字符不属于ASCII返回,iex就打印它的code point。
175
198
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
+
179
206
``` elixir
180
207
iex> to_char_list " hełło"
181
208
[104 , 101 , 322 , 322 , 111 ]
@@ -186,4 +213,5 @@ iex> to_string :hello
186
213
iex> to_string 1
187
214
" 1"
188
215
```
189
- 注意这些函数是多态的。它们不但转化字符列表和字符串,还能转化字符串和整数,等等。
216
+
217
+ 注意这些函数是多态的。它们不但可以将字符列表转化为字符串,还能转化整数、原子等为字符串。
0 commit comments