首页 > 代码库 > Erlang二进制模式匹配
Erlang二进制模式匹配
Erlang的模式匹配用来处理二进制数据可谓是得心应手。不仅直观,而且超乎想象的简单。在C++中,处理二进制数据首先要管理缓冲区。然后再按字节进行操作,如果要处理的数据不是按字节对齐,则需要进行位移等操作。操作过程复杂又难懂,如果再没有注释,那对于维护这段代码的人来说简直就是噩梦。例如:操作一串保存了RGB颜色值序列的二进制串,在C++中要这样操作:
struct RGB { char R; char G; char B; }; vector<RGB> rgbVector; for(int i = 0; i < pixelsLen; i = i + 3) { RGB rgb; rgb.R = pixels[i]; rgb.G = pixels[i + 1]; rgb.B = pixels[i + 2]; rgbVector.push_back(rgb); }
首先要定义RGB结构,假设RGB数据流保存在pixels指向的地址中,然后通过循环解析出相应的RGB值,然后保存在数组中。而实现同等功能的Erlang却只需要一行就搞定,如下:
Pixels = <<213,45,132,64,76,0,0,234,32,15>>.
RGB = [ {R,G,B} || <<R:8,G:8,B:8>> <= Pixels ].
是不是很简单,而且上面所举的例子中,RGB的值刚好是一个字节,所以在C++中处理方式相对比较简单。下面看一个更复杂的实例,这个例子在Erlang官方文档中有介绍,你可以戳这里.这是一个解析IP数据包头的例子(如果不了解IP包头戳这里),在Erlang中只需要这样:
-define(IP_VERSION, 4). -define(IP_MIN_HDR_LEN, 5). DgramSize = size(Dgram), case Dgram of <<?IP_VERSION:4, HLen:4, SrvcType:8, TotLen:16, ID:16, Flgs:3, FragOff:13, TTL:8, Proto:8, HdrChkSum:16, SrcIP:32, DestIP:32, RestDgram/binary>> when HLen>=5, 4*HLen=<DgramSize -> OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN), <<Opts:OptsLen/binary,Data/binary>> = RestDgram, ... end.
看到这段代码你是不是很容易得就明白了,它要干什么,各种值的位置、占用的空间大小以及所代表的意思都一目了然,看着真的是有一种心旷神怡的感觉。我第一次看到这样的处理方式时,我很激动,原来处理二进制数据可以如此简单直观。虽然简单易懂,但是我还是要在这里简单的介绍一下,首先通过size函数获取整个IP数据包的长度。然后对整个数据包进行二进制模式匹配。根据IP包头的格式,0-3位是协议版本号,对于IPV4来说这里应该是4,所以在模式匹配的时候直接<<4:4, Rest/binary>>就可以匹配到版本号了。上面的例子中把4定义成一个宏。接下来的4-7位是IP首部的长度。后面的我就不详细介绍了,具体IP头部参考这里。我在这里举这个例子并不是为了真的用Erlang去解析IP包,仅仅是为了说明Erlang解析二进制的优点。
<<DataLen:16,Command:16,RData/binary>> = Data
其中Data是原始数据包,DataLen是包长度,Command是消息码,RData是包体。当然在实际项目中还要考虑粘包,半包以及相应的错误处理部分,这里仅仅提供了二进制模式匹配的方式。