我继承了具有以下规范的二进制文件格式:
| 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>>.
这两种解决方案都让我同样感到沮丧。我仍然怀疑有一种很好的方式 处理这种情况。有吗?
答案 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}.