parsing text and binary files
(form: http://wagerlabs.com/ openpoker author)
Erlang最初主要是针对电信应用开发,因此一个必须要完成的工作就是text和binary数据的转化。
在我们的通讯服务器开发课程中,解析text和binary数据,是我们经常要做的事情。再这里我要向大家讲述如何用erlang高效的处理这些事情。
选择Strings 或 binaries
Erlang本身没有string类型,其采用integer list来表现string,比如:”a string”,对应的list为[$a, $ , $s, $t, $r, $i, $n, $g]。在32位的Erlang虚拟机中,integer为4字节,因此在list的实现内部,每个元素除去4字节存储自身Value,同时还需要4字节作为指针指向下一个元素,因此要表现一个string的字符,我们需要付出8字节的代价,而在64位的erlang虚拟机中,这个代价还要翻倍。
为什么要关心这些呢?
在实际的开发中,每个同服务器的连接都需要分配一定的内存,用来发送接收数据。很多的互联网协议,比如XML其通过字符来表现。比如我们接收10K的XML消息,如果将这些消息转化为String进行解析,那么消息所占用的内存将为80K或者160左右,显然这是非常低效的。
当服务器的并发连接为成百上千时,减少每个连接所消耗的内存,将变的非常有意义。任何数据再经过我们的代码处理成另一种Erlang的数据结构后,原有的数据将进行垃圾回收。如果我们产生的垃圾越少,那么我们的垃圾回收越高效,我们的服务器也就越高效。
因此我们需要把我们通过socket接收的binary保持原样,不要尝试将其转化为strings。binary的解析是非常迅速的,并且具有非常方便的构造和匹配语法。而String却没有这些好处。
Erlang中所有的输入输出函数都可以操作binary数据。同一个程序,采用binary作为内部数据处理的程序肯定比采用String要高效。
好了,让我们用锤子将Erlang String牢牢的钉在坟墓里吧。
将文本文件作为binary处理
假设我们要解析一个以逗号分割的文本文件。我们将解析每一行中的文本,将分割出的各个内容放入list,同时所有的行也放入一个list。我们要处理的文件不是很大,因此我们可以一次加载整个文件。下面是代码的一个最初样子:
-module(act).
-compile([export_all]).
parse(Filename) when is_list(Filename) ->
{ok, Bin} = file:read_file(Filename),
parse(Bin); %% 作者此处原为”.”,应该为”;”
parse(Bin) when is_binary(Bin) ->
parse(Bin, [], [], []).
上面代码中的元素为空的list,作为初始参数,其用来保存累积的值(称为Accumulator,在Erlang这样的FP编程中,其作为参数也用来保存状态)。三个[],分别对应Field,Line和Acc,Field用来保存我们正在处理的字段(一个string),Line用来保存我们正在处理行的field 列表,Acc则是保存我们处理的Line的列表。
上面的代码中有两个parse函数(确切的说是一个parse函数,两个clause):第一个parse的参数为要操作的文件名称(string, a list of integers), 第二个parse函数的参数为binary,即我们读取的文件内容。这两个完成不同功能函数同样的名称,仅仅是通过guard来进行区别。你也可以将第二个函数命名为parse1或do_parse,这样更清晰一些。
Erlang中的函数通过函数名和参数个数来进行区分,采用fun_name/num_args的形式来表示一个函数。由此可知,parse/0, parse/1是不同的函数。我们上面代码中两个parse函数其实是一个函数,仅仅通过guard [...]
Tags: erlang 作者 cheng
No Comments »