我正在开发一个涉及大量字节级操作的项目。我希望该库接受二进制文件作为输入,但使用Enum
上的函数通常很方便。我最终得到了许多具有两个保护定义的函数,一个用于when is_binary
,另一个用于when is_list
:
@doc """
Convert a binary or list of bytes to a hexadecimal string representation,
## Examples
iex> Bytes.to_hex <<1,2,3,253,254,255>>
"010203fdfeff"
iex> Bytes.to_hex [1,2,3,253,254,255]
'010203fdfeff'
"""
def to_hex(bytes) when is_binary(bytes) do
:binary.bin_to_list(bytes) |> to_hex |> :binary.list_to_bin
end
def to_hex(bytes) when is_list(bytes) do
hexes = Enum.map bytes, &byte_to_hex/1
Enum.join(hexes) |> String.downcase |> :binary.bin_to_list
end
defp byte_to_hex(byte) do
Integer.to_char_list(byte, 16) |> :string.right(2, ?0)
end
此外,列表版本末尾有:binary.bin_to_list
,后面是字符串版本末尾的:binary.list_to_bin
,导致多余的工作。
我真的希望能够对二进制文件使用相同的抽象。我最初尝试通过实现BitString
的协议来实现这一点:
defimpl Enumerable, for: BitString do
def count(coll) do
{:ok, byte_size(coll)}
end
def member?(_coll, _val) do
{:error, __MODULE__}
end
def reduce(_, {:halt, acc}, _fun) do
{:halted, acc}
end
def reduce(bin, {:suspend, acc}, fun) do
{:suspended, acc, &reduce(bin, &1, fun)}
end
def reduce(<<>>, {:cont, acc}, _fun) do
{:done, acc}
end
def reduce(<< h :: binary-size(1), t :: binary >>, {:cont, acc}, fun) do
reduce(t, fun.(h, acc), fun)
end
end
这适用于count
,member
和reduce
案例:
test "counts bytes" do
assert Enum.count(<<0, 1, 2>>) == 3
end
test "reports members" do
assert Enum.member?(<<0, 1, 2>>, <<3>>) == false
assert Enum.member?(<<0, 1, 2>>, <<2>>) == true
end
test "reduces binaries" do
reducer = fn(b, acc) -> b <> acc end
assert Enum.reduce("abc", "", reducer) == "cba"
end
但是,某些Enum
函数会调用:lists
上的函数;例如,Enum.map
调用:lists.reverse
,因此这会产生不同的输出:
test "maps binaries" do
mapper = fn(<<b>>) -> <<b + 1>> end
assert Enum.map(<<0, 1, 2 >>, mapper) == <<1, 2, 3>>
end
1) test maps binaries
Assertion with == failed
code: Enum.map(<<0, 1, 2>>, mapper) == <<1, 2, 3>>
lhs: [<<1>>, <<2>>, <<3>>]
rhs: <<1, 2, 3>>
处理这种情况最常用的方法是什么?我应该简单地与警卫保持联系并实施清单&lt; - &gt;二进制转换?我应该为功能创建特定模块,例如List
是Enum
吗?
修改
我以为我可以处理额外的清单&lt; - &gt;通过使二进制文件成为基本案例进行二进制转换:
def to_hex(bytes) when is_binary(bytes) do
bytes
|> :binary.bin_to_list
|> Enum.map(&byte_to_hex/1)
|> Enum.join
end
def to_hex(bytes) when is_list(bytes) do
bytes |> :binary.list_to_bin |> to_hex |> :binary.bin_to_list
end
defp byte_to_hex(byte) do
Integer.to_string(byte, 16) |> String.rjust(2, ?0) |> String.downcase
end
但是,当然,此版本中仍有相同数量的list_to_bin
和bin_to_list
来电,只是四处移动。有没有办法解决这个问题?
答案 0 :(得分:0)
看起来很奇怪你会得到二进制文件,而这些二进制文件只能枚举二进制文件。对我来说,他们应该是整数。就像一个char列表。
然后像Enum.map
这样的函数会按预期工作。