I was wondering how I would work with hex strings in Elixir. Specifically, I'm interested in converting from Hex to ASCII.
In Ruby, an implementation of this may be:
["001C7F616A8B002128C1A33E8100"].pack('H*').gsub(/[^[:print:]]/, '.')
How would I accomplish this task with 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.
If you are going to raise on an invalid string, don't bother checking, just call decode16 directly as it also checks the size.
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. 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.
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:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With