IOData

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kkx12138/article/details/51988926

看项目代码的时候发现写入file的Info不是一个扁平化的list, 所以感觉很奇怪。 就看了一下file的文档
file:write_file(FileName, Bytes) -> ok|{error, Reason}
Bytes = iodata()
iodata() 文档中是这么定义的:
iodata(): iolist()|binary
binary():<<_:_*8>>
iolist(): maybe_improper_list(byte() | binary() | iolist(), binary() | [])

1.首先是 binary():<<_:_*8>>

一个8bit为单位的串。

1>is_binary(<<1:1>>).
false
2>is_binary(<<1:24>>).
true

2.那么iolist() 呢?

>1 iolist是什么:

从定义和mryufeng的《iolist 跟 list 有什么区别》中总结为
1. []
2. binary
3. maybe_improper_list列表,
就是每个元素是int(0-255) 或者是binary(bitsize%8 == 0)或者是iolist(递归定义)

我们可以测试一下
%% 测试数据

1> iolist_size([255]).
1
2> iolist_size([257]).
** exception error: bad argument
in function iolist_size/1
called as iolist_size([257])
3> iolist_size(<<1:1>>).
** exception error: bad argument
in function iolist_size/1
called as iolist_size(<<1:1>>)
4> iolist_size(<<1:8>>).
1
5> iolist_size([1, 3|<<1:8>>]).
3
6> iolist_size([1, <<1:8>>, [1, 2|<<2:8>>]]).
5
7> iolist_size([1, 3|4]).
** exception error: bad argument
in function iolist_size/1
called as iolist_size([1,3|4])

但是现在对于定义还有两个疑问
1. improper list 是什么意思呢?
improper list 是指list不是递归定义的, 具体看stackoverflow 答:http://stackoverflow.com/questions/1919097/functional-programming-what-is-an-improper-list/1922724#1922724
2. 为什么7>是不行的呢??
不符合iolist 的定义

>2 iolist使用的场景:

mryufeng在《iolist跟list有什么区别》中说:
iolist的作用是用于往port送数据的时候。由于底层的系统调用writev支持向量写, 就避免了无谓的iolist_to_binary这样的扁平化操作, 避免了内存拷贝() 极大的提高了效率,建议多用。

这是什么意思呢?? 看坚强2002的答案:http://www.cnblogs.com/me-sa/archive/2012/01/31/erlang0034.html
简单翻译learn you some erlang << Buckets of Sockets>> 一文的开篇:
IO Lists

在这本书早些篇章中,我提到我们可以用strings(lists of integers) 和 binaries(一种存储数据的二进制数据结构). 发送”Hello World”, 可以用string
的方式像”Hello World”, 也可以用binaries方式像<<”Hello World”>>. 类似的表示方式,类似的结果。

不同的地方在于你组装数据的方式, 一个string就像是整数链表:对于每一个字符,你需要保存字符本身加上和链表其余部分的连接。此外,如果你想增加一个
元素在一个list上, 无论是在中间还是在末尾,你都必须要遍历到你要修改的点上,然后才能添加元素。

但是当你在前面加的时候,情况就不是这样了:
A = [a]
B = [b|A] = [b,a]
C = [c|B] = [c,b,a]

就像上面这样在前面加的时候, 不管是A,或者是B还是C 都不必要重写。 C的表达方式也可以看成是[c,b,a], [c|B], 或者[c,|[b|[a]]],等。
在最后一种情况中, 在列表末尾的A和它刚开始声明的时候是一样的, 对于B来讲也是一样的。
下面是它追加的样子:

A = [a]
B = A ++ [b] = [a] ++ [b] = [a|[b]]
C = B ++[c] = [a|[b]] ++ [c] = [a|[b|[c]]]

看到所有的这些都重写了吧??当我们创建B的时候,我们必须重写A。 当我们写C,我们必须重写B(包括[a])。 如果我们想以同样的方式增加D,我们需要重写C.
在长字符串的时候,这个成为了效率很低的方式, 这也留下了很多垃圾,留待erlang VM去清理。

在binaries 这里, 事情并没有这么坏
A = <<”a”>>
B = << A/binary, “b”>> = <<”ab”>>
C = << B/binary, “c”>> = <<”abc”>>

这里, binaries 知道它们自己的长度,而且数据可以在常量时间内加入到其中。这远比lists 要好。 他们也更紧凑。 基于这些原因,我们常常坚持在未来的
文件中使用二进制文件。

但是, 它也有几个缺点:Binaries 注定要用特定的方式处理问题, 还有就是在修改binaries和分开他们的时候也是有花销的。此外,我们需要在代码工作中在strings,binaries 和 个别的字符串之间。不断在类型之间转换也是一个麻烦。

从这点上来看, IO Lists 是我们的救星。IO List 是一种很奇怪的数据结构。 它要么是bytes(0-255 的整数), binaries 或者其他的IO 列表。 这就意味着那些接受IOlist 的函数会接受类似于[ H , e, [$I, <<”Io”>>,”“], [[[“W”,”o”], <<”rl”>>]]|[<<”d>>]]. 当这种情况发生的时候, erlang 的VM 会扁平化这个列表, 当它需要去或者一连串的字符Hello World的时候。

那什么样的函数接受IO List呢? 多数函数都是与输出相关的。file 模块, TCP 和 UDP模块.还有一些库函数, 比如一些来自Unicode 模块和re模块中的所有函数
等。在shell中尝试前面的hello world IO list, io:format(“~s~n”,[IoList])。 同样也能工作。

总而言之, 这是一种很好的方式去避免当不可变的数据机制动态生成内容输出的时候的问题。

同样做一个总结:
1. 在lists头部追加内容是非常快速的, 在尾部或者中间加入需要遍历
2. 使用binaries可以在常量时间完成追加(底层相关why?) 但是:1 修改和split需要花销 2. 字符串和二进制转化麻烦
3. iolist是一种很好的混搭风去解决上面的问题。erlang VM 会扁平化输出。
4. iolist是单词赋值约束下,动态构建字符串内容输出的好方法。

猜你喜欢

转载自blog.csdn.net/kkx12138/article/details/51988926