读取16位little-endian,然后在erlang中解析为bittring

时间:2014-12-28 21:30:49

标签: erlang endianness

我继承了具有以下规范的二进制文件格式:

  |     F      | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
0:| Status bit |        ------ 15 - bit unsigned integer -----------
1:| Status bit |        ----  uint:10  ----            | ---- uint:5 ---- 

Erlang中的位匹配非常棒。所以我喜欢做这样的事情:

<<StatBit1:1, ValA:15/unsigned>> = <<2#1000000000101010:16>>.
<<StatBit2:1, ValB:10/unsigned, ValC:5/unsigned>> = <<2#0000001010100111:16>>.

问题是我需要处理的文件是以8位小端保存的 惯例。所以上面例子中文件的前8位是 00101010然后1000000 e.t.c。

{ok, S} = file:open("datafile", [read, binary, raw]).
{ok, <<Byte1:8, Byte2:8, Byte3:8, Byte4:8>>} = file:read(S,4).
io:format(
     " ~8.2.0B | ~8.2.0B | ~8.2.0B | ~8.2.0B ~n ", 
     [Byte1, Byte2, Byte3, Byte4]).

# 00101010 | 1000000 | 10100111 | 00000010
# ok

所以我采用读取和交换字节:

<<StatBit1:1, ValA:15/unsigned>> = <<Byte2:8, Byte1:8>>.
<<StatBit2:1, ValB:10/unsigned, ValC:5/unsigned>> = <<Byte4:8, Byte3:8>>.

或者我可以阅读16位小端,然后解析&#34;它:

{ok, S} = file:open("datafile", [read, binary, raw]).
{ok, <<DW1:16/little, DW2:16/little>>} = file:read(S,4).
<<StatBit1:1, ValA:15/unsigned>> = <<DW1:16>>.
<<StatBit2:1, ValB:10/unsigned, ValC:5/unsigned>> = <<DW2:16>>.

这两种解决方案都让我同样感到沮丧。我仍然怀疑有一种很好的方式 处理这种情况。有吗?

3 个答案:

答案 0 :(得分:1)

我首先考虑更改生成这些文件的应用程序,以便按网络(big-endian)顺序写入数据。如果那是不可能的,那么你就像你已经在做的那样陷入了字节交换。您可以将交换包装成一个函数,使其远离解码逻辑:

byteswap16(F) ->
    case file:read(F, 2) of
        {ok, <<B1:8,B2:8>>} -> {ok, <<B2:8,B1:8>>};
        Else -> Else
    end.

或者,也许你可以预处理文件。您在评论中提到文件很大,所以这可能对您的情况不实用,但如果每个文件都适合内存,您可以使用file:read_file/1来读取整个文件,然后使用一个文件预处理内容。二元理解:

byteswap16(Filename) ->
    {ok,Bin} = file:read_file(Filename),
    << <<B2:8,B1:8>> || <<B1:8,B2:8>> <= Bin >>.

这两种解决方案都假设整个文件都是用16位小端格式编写的。

答案 1 :(得分:1)

为了解释为什么二进制语法(原样)无法解决您的问题,请考虑文件中的位确实是7,... 0,F,E,...... 8。状态位在F中,但是如果你说&#34;下一个字段是15位长,并且是一个小端无符号整数&#34;,你将获得位7,... 0,F ,E,... 9(接下来的15位),然后将被解释为小端。您无法表达您想跳过F位并改为使用E-8这一事实,然后返回并选择F位作为状态。如果您可以先将字节交换文件,例如使用&#34; dd if = infile of = outfile conv = swab&#34;,你可以让你的生活变得更轻松。

答案 2 :(得分:0)

你尝试过类似的东西: [编辑]进行一些修正,但我无法在我的标签上进行测试。

decode(<<A:8, 1:1, B:7>>) -> {status1, B*256+A};
decode(<<A:3, C:5, 0:1, B:7>>) -> {status2, B*8+A, C}.