1
1
12-I/O
2
2
======
3
- [ I/O模块] ( #121-io%E6%A8%A1%E5%9D%97 ) <br />
4
- [ 文件模块] ( #122-%E6%96%87%E4%BB%B6%E6%A8%A1%E5%9D%97 ) <br />
5
- [ 路径模块] ( #123-%E8%B7%AF%E5%BE%84%E6%A8%A1%E5%9D%97 ) <br />
6
- [ 进程和组长] ( #124-%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BB%84%E9%95%BF ) <br />
7
- [ * iodata* 和* chardata* ] ( #125-iodata%E5%92%8Cchardata ) <br />
8
-
9
- 本章简单介绍Elixir的输入、输出机制,以及相关的模块,如[ IO] ( http://elixir-lang.org/docs/stable/elixir/IO.html ) ,
3
+ [ I/O模块] ( #121-io%E6%A8%A1%E5%9D%97 )
4
+ [ 文件模块] ( #122-%E6%96%87%E4%BB%B6%E6%A8%A1%E5%9D%97 )
5
+ [ 路径模块] ( #123-%E8%B7%AF%E5%BE%84%E6%A8%A1%E5%9D%97 )
6
+ [ 进程和组长] ( #124-%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BB%84%E9%95%BF )
7
+ [ * iodata* 和* chardata* ] ( #125-iodata%E5%92%8Cchardata )
8
+
9
+ 本章简单介绍Elixir的输入、输出机制以及相关的模块,
10
+ 如[ IO] ( http://elixir-lang.org/docs/stable/elixir/IO.html ) ,
10
11
[ 文件] ( http://elixir-lang.org/docs/stable/elixir/File.html )
11
12
和[ 路径] ( http://elixir-lang.org/docs/stable/elixir/Path.html ) 。
12
13
15
16
##12 .1-IO模块
16
17
IO模块是Elixir语言中读写标准输入、输出、标准错误、文件、设备的主要机制。
17
18
使用该模块的方法颇为直接:
18
- ```
19
+ ``` elixir
19
20
iex> IO .puts " hello world"
20
21
" hello world"
21
22
:ok
@@ -26,16 +27,17 @@ yes or no? yes
26
27
27
28
IO模块中的函数默认使用标准输入输出。
28
29
我们也可以传递``` :stderr ``` 来指示将错误信息写到标准错误设备上:
29
- ```
30
+ ``` elixir
30
31
iex> IO .puts :stderr , " hello world"
31
32
" hello world"
32
33
:ok
33
34
```
34
35
35
36
#12 .2-文件模块
36
37
文件模块包含了可以让我们读写文件的函数。
37
- 默认情况下文件是以二进制模式打开,它要求程序员使用特殊的``` IO.binread/2 ``` 和``` IO.binwrite/2 ``` 函数来读写文件:
38
- ```
38
+ 默认情况下文件是以二进制模式打开,它要求程序员使用特殊的``` IO.binread/2 ```
39
+ 和``` IO.binwrite/2 ``` 函数来读写文件:
40
+ ``` elixir
39
41
iex> {:ok , file} = File .open " hello" , [:write ]
40
42
{:ok , # PID<0.47.0>}
41
43
iex> IO .binwrite file, " world"
@@ -47,20 +49,22 @@ iex> File.read "hello"
47
49
```
48
50
49
51
文件可以使用``` :utf8 ``` 编码打开,然后就可以被IO模块中其他函数使用了:
50
- ```
52
+ ``` elixir
51
53
iex> {:ok , file} = File .open " another" , [:write , :utf8 ]
52
54
{:ok , # PID<0.48.0>}
53
55
```
54
56
55
- 除了打开、读写文件的函数,文件模块还有许多函数来操作文件系统。这些函数根据Unix功能相对应的命令命名。
56
- 如``` File.rm/1 ``` 用来删除文件;``` File.mkdir/1 ``` 用来创建目录;``` File.mkdir_p/1 ``` 创建目录并保证其父目录一并创建;
57
+ 除了打开、读写文件的函数,文件模块还有许多函数来操作文件系统。
58
+ 这些函数根据Unix功能相对应的命令命名。
59
+ 如``` File.rm/1 ``` 用来删除文件;``` File.mkdir/1 ```
60
+ 用来创建目录;``` File.mkdir_p/1 ``` 创建目录并保证其父目录一并创建;
57
61
还有``` File.cp_r/2 ``` 和``` File.rm_rf/2 ``` 用来递归地复制和删除整个目录。
58
62
59
63
60
- 你还会注意到文件模块中,函数一般有一个名称类似的版本 。区别是名称上一个有!(bang)一个没有。
64
+ 你还会注意到文件模块中,一般函数都有一个名称类似的版本 。区别是名称上一个有!(bang)一个没有。
61
65
例如,上面的例子我们在读取“hello”文件时,用的是不带!号的版本。
62
66
下面用例子演示下它们的区别:
63
- ```
67
+ ``` elixir
64
68
iex> File .read " hello"
65
69
{:ok , " world" }
66
70
iex> File .read! " hello"
@@ -71,49 +75,51 @@ iex> File.read! "unknown"
71
75
** (File .Error ) could not read file unknown: no such file or directory
72
76
```
73
77
74
- 注意看,当文件不存在时,带!号的版本会报错。就是说不带!号的版本能照顾到模式匹配出来的不同的情况。
75
- 但有的时候,你就是希望文件在那儿,!使得它能报出有意义的错误。
78
+ 注意看,当文件不存在时,带!号的版本会报错。就是说不带!号的版本能照顾到模式匹配出来的不同情况。
79
+ 但有的时候,你就是希望文件在那儿,!使得它能报出有意义的错误。
80
+
76
81
因此,不要写:
77
- ```
82
+ ``` elixir
78
83
{:ok , body} = File .read (file)
79
84
```
80
85
81
86
相反地,应该这么写:
82
- ```
87
+ ``` elixir
83
88
case File .read (file) do
84
89
{:ok , body} -> # handle ok
85
90
{:error , r} -> # handle error
86
91
end
87
92
```
88
93
或者
89
- ```
94
+ ``` elixir
90
95
File .read! (file)
91
96
```
92
97
93
98
## 12.3-路径模块
94
99
文件模块中绝大多数函数都以路径作为参数。通常这些路径都是二进制,可以被路径模块提供的函数操作:
95
- ```
100
+ ``` elixir
96
101
iex> Path .join (" foo" , " bar" )
97
102
" foo/bar"
98
103
iex> Path .expand (" ~/hello" )
99
104
" /Users/jose/hello"
100
105
```
101
106
102
- <br />
103
107
104
108
有了以上介绍的几个模块和函数,我们已经能对文件系统进行基本的IO操作。
105
- 下面将讨论IO令人好奇的高级话题 。这部分不是写Elixir程序必须掌握的,可以跳过不看。
106
- 但是如果你大概地看看,可以了解一下IO是如何在VM上实现以及其它一些有趣的内容 。
109
+ 下面将讨论I/O模块中令人好奇的高级话题 。这部分不是写Elixir程序必须掌握的,可以跳过不看。
110
+ 但是如果你大概地浏览一下,可以了解IO是如何在VM上实现以及其它一些有趣的内容 。
107
111
108
112
## 12.4-进程和组长
109
113
你可能已经发现,``` File.open/2 ``` 函数返回了一个包含PID的元祖:
110
- ```
114
+ ``` elixir
111
115
iex> {:ok , file} = File .open " hello" , [:write ]
112
116
{:ok , # PID<0.47.0>}
113
- ```
114
- 这是因为IO模块实际上是同进程协同工作的。当你调用``` IO.write(pid, binary) ``` 时,IO模块将发送一条消息给执行操作的进程。
117
+ ```
118
+
119
+ I/O模块实际上是同进程协同工作的。当你调用``` IO.write(pid, binary) ``` 时,
120
+ I/O模块将发送一条消息给执行操作的进程。
115
121
让我们用自己的代码表述下这个过程:
116
- ```
122
+ ``` elixir
117
123
iex> pid = spawn fn ->
118
124
.. .> receive do: (msg - > IO .inspect msg)
119
125
.. .> end
@@ -123,21 +129,23 @@ iex> IO.write(pid, "hello")
123
129
** (ErlangError ) erlang error: :terminated
124
130
```
125
131
126
- 调用``` IO.write/2 ``` 之后,可以看见打印出了发给IO模块的请求。然而因为我们没有提供某些东西,这个请求失败了。
132
+ 调用``` IO.write/2 ``` 之后,可以看见打印出了发给IO模块的请求。
133
+ 然而因为我们没有提供某些东西,这个请求失败了。
127
134
128
- [ StringIO模块] ( http://elixir-lang.org/docs/stable/elixir/StringIO.html ) 提供了一个基于字符串的IO实现:
129
- ```
135
+ [ StringIO模块] ( http://elixir-lang.org/docs/stable/elixir/StringIO.html )
136
+ 提供了一个基于字符串的IO实现:
137
+ ``` elixir
130
138
iex> {:ok , pid} = StringIO .open (" hello" )
131
139
{:ok , # PID<0.43.0>}
132
140
iex> IO .read (pid, 2 )
133
141
" he"
134
142
```
135
143
136
- Erlang虚拟机用进程给IO设备建模 ,允许同一个网络中的不同节点通过交换文件进程,实现节点间的文件读写。
137
- 在所有IO设备之中,有一个特殊的进程,称作组长(group leader)。
144
+ Erlang虚拟机用进程给I/O设备建模 ,允许同一个网络中的不同节点通过交换文件进程,
145
+ 实现节点间的文件读写。 在所有IO设备之中,有一个特殊的进程,称作组长(group leader)。
138
146
139
147
当你写东西到标准输出,实际上是发送了一条消息给组长,它把内容写给* STDIO文件表述者* :
140
- ```
148
+ ``` elixir
141
149
iex> IO .puts :stdio , " hello"
142
150
hello
143
151
:ok
@@ -151,11 +159,12 @@ hello
151
159
152
160
## 12.5-* iodata* 和* chardata*
153
161
在以上所有例子中,我们都用的是二进制/字符串方式读写文件。
154
- 在“二进制、字符串和字符列表”那章里,我们注意到字符串就是普通的bytes,而字符列表是code points的列表。
162
+ 在“二进制、字符串和字符列表”那章里,我们注意到字符串就是普通的bytes,
163
+ 而字符列表是code points(字符码)的列表。
155
164
156
-
157
- IO模块和文件模块中的函数接受列表作为参数。这也就算了,其实还可以接受混合类型的列表,里面是整形 、二进制都行:
158
- ```
165
+ I/O模块和文件模块中的函数接受列表作为参数。
166
+ 还可以接受混合类型的列表,里面内容是整形 、二进制都行:
167
+ ``` elixir
159
168
iex> IO .puts ' hello world'
160
169
hello world
161
170
:ok
@@ -164,10 +173,12 @@ hello world
164
173
:ok
165
174
```
166
175
167
- 尽管如此,有些地方还是要注意。一个列表可能表示一串byte,或者一串字符。用哪一种看IO设备是怎么编码的。
168
- 如果不指明编码,文件就以raw模式打开,这时候只能用文件模块里bin* 开头(二进制版)的函数对其进行操作。
169
- 这些函数接受* iodata* 作为参数,即,它们期待一个整数值的列表,用来表示byte或二进制。
176
+ 尽管如此,有些地方还是要注意。一个列表可能表示一串byte,或者一串字符。
177
+ 用哪一种需要看I/O设备是怎么编码的。
170
178
171
- 尽管只是细微的差别,但你只需要考虑那些细节,如果你打算传递列表给那些函数。
172
- 底层的bytes已经可以表示二进制,这种表示就是raw的。
179
+ 如果不指明编码,文件就以raw模式打开,这时候只能用文件模块里bin* 开头(二进制版)
180
+ 的函数对其进行操作。
181
+ 这些函数接受* iodata* 作为参数,即,它们期待一个整数值的列表,用来表示byte或二进制。
173
182
183
+ 尽管只是细微的差别,如果你打算传递列表给那些函数,你需要考虑那些细节。
184
+ 底层的bytes可以表示二进制,这种表示就是raw的。
0 commit comments