[英]How to pack / unpack a hex string (high nibble first) with Elixir
I was wondering how I would work with hex strings in Elixir. 我想知道如何在Elixir中使用十六进制字符串。 Specifically, I'm interested in converting from Hex to ASCII. 具体来说,我对将十六进制转换为ASCII感兴趣。
In Ruby, an implementation of this may be: 在Ruby中,此实现可能是:
["001C7F616A8B002128C1A33E8100"].pack('H*').gsub(/[^[:print:]]/, '.')
How would I accomplish this task with Elixir? 我如何用Elixir完成这项任务? I have tried: 我试过了:
<<00, 01, C7, F6...>>
but this isn't a correct representation of the hex for a string. 但这不是字符串十六进制的正确表示形式。 Thanks for your time and assistance! 感谢您的时间和协助!
So I've made some progress but am currently struggling with the recursion aspect of this. 因此,我取得了一些进展,但是目前正在为递归方面做着努力。
This is my solution thus far: 到目前为止,这是我的解决方案:
defmodule ElixirNetworkTools do
def decode(payload) do
upper_payload = String.upcase payload
case Base.decode16(upper_payload) do
:error -> decode_with_nonprintable_characters(payload)
{:ok, decoded_payload} -> decoded_payload
end
|> IO.write
end
def decode_with_nonprintable_characters(payload) do
String.chunk(payload, ~r/\w{2}/)
|> Enum.each(fn(byte) ->
case Base.decode16(byte) do
:error -> '.'
{:ok, decoded_payload} -> decoded_payload
end
end)
end
end
Here is another solution to the problem. 这是解决该问题的另一种方法。 A couple things before we start: 我们开始之前的几件事:
You can pass case: :mixed
to Base.decode16/2
: Base.decode16(string, case: :mixed)
, for this reason, you don't need do upcase before. 您可以将case: :mixed
传递给Base.decode16/2
: Base.decode16(string, case: :mixed)
,因此,您不需要大写。
If you are going to raise on an invalid string, don't bother checking, just call decode16 directly as it also checks the size. 如果您要提出一个无效的字符串,不要打扰检查,只需直接调用decode16,因为它还会检查大小。
This means we can start with: 这意味着我们可以从以下内容开始:
decoded = Base.decode16!(string, case: :mixed)
Now you need to replace non-printable characters. 现在,您需要替换不可打印的字符。 Don't use String.printable?/1
because it is about UTF-8 and not ASCII. 不要使用String.printable?/1
因为它是关于UTF-8而不是ASCII的。 We need to implement our own function but what makes more sense: to raise or replace them? 我们需要实现自己的功能,但是更有意义的是:提出或替换它们? It seems it must be considered an error if someone send invalidate data? 如果有人发送无效数据,似乎必须将其视为错误? If that is the case: 如果是这样的话:
def validate_ascii!(<<h, t::binary>>) when h <= 127 do
validate_ascii!(t)
end
def validate_ascii!(<<>>) do
true
end
def validate_ascii!(rest) do
raise "invalid ascii on string starting at: #{rest}"
end
Alternatively you can just remove the last clause and it fail too. 或者,您可以只删除last子句,它也会失败。
Now we can put it together: 现在我们可以将其放在一起:
decoded = Base.decode16!(string, case: :mixed)
validate_ascii!(decoded)
decoded
EDIT: If you need to replace non-ascii by dots: 编辑:如果您需要用点替换非ASCII:
def keep_ascii(<<h, t::binary>>, acc) when h <= 127 do
keep_ascii(t, acc <> <<h>>)
end
def keep_ascii(<<_, t::binary>>, acc) do
keep_ascii(t, acc <> ".")
end
def keep_ascii(<<>>, acc) do
acc
end
The solution ended up being as follows, though if there is a cleaner or better solution I would be very interested in knowing it. 解决方案最终如下,尽管如果有更清洁或更完善的解决方案,我会对了解它非常感兴趣。
defmodule ElixirNetworkTools do
@doc """
The decode function takes a hexadecimal payload, such as one generated
by Snort, and returns the ASCII representation of the string.
## Example
iex> ElixirNetworkTools.decode("436F6E74656E742D4C656E6774683A203132")
{:ok, "Content-Length: 12"}
"""
def decode(payload) do
case _validate_length_of_snort(payload) do
:error -> raise "Invalid length hex string. Must be even length. Exiting"
_ -> nil
end
decoded = String.upcase(payload)
|> _do_decode
|> to_string
{:ok, decoded}
end
@doc """
Internal function used to manually process the hexadecimal payload,
and builds a char list of the printable characters. If a character is
not printable, we instead use periods.
## Example
iex> ElixirNetworkTools._do_decode("436F6E74656E742D4C656E6774683A203132")
["Content-Length: 12"]
"""
def _do_decode(payload) do
Base.decode16!(payload)
|> String.chunk(:printable)
|> Enum.map(fn(chunk) ->
case String.printable? chunk do
true -> chunk
_ -> "."
end
end)
end
@doc """
Internal function used to validate the length of the hexadecimal payload.
Hexadecimal strings should have an even number of characters.
## Example
iex> ElixirNetworkTools._validate_length_of_snort("436F6E74656E742D4C656E6774683A203132")
:ok
"""
def _validate_length_of_snort(payload) do
String.length(payload)
|> rem(2)
|> case do
0 -> :ok
_ -> :error
end
end
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.