2015-05-07 90 views
4

我想知道如何在Elixir中使用十六进制字符串。具体而言,我有兴趣将Hex转换为ASCII。如何用Elixir打包/解压十六进制字符串(高位半字节)

在Ruby中,这种实现可能是:

["001C7F616A8B002128C1A33E8100"].pack('H*').gsub(/[^[:print:]]/, '.') 

我将如何实现与灵药这项任务?我曾尝试过:

<<00, 01, C7, F6...>> 

但这不是字符串的十六进制的正确表示形式。感谢您的时间和协助!

所以我取得了一些进展,但我目前正在努力与此递归方面。

这是我的解决方案迄今:

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 

回答

1

这是另一种解决方案。我们前一对夫妇的事情开始:

  • 你可以通过case: :mixedBase.decode16/2Base.decode16(string, case: :mixed),因为这个原因,你不需要做upcase之前。

  • 如果你打算提出一个无效的字符串,不要麻烦检查,直接调用decode16,因为它也检查大小。

这意味着我们可以开始:

decoded = Base.decode16!(string, case: :mixed) 

现在你需要更换非打印字符。不要使用String.printable?/1,因为它是关于UTF-8而不是ASCII。我们需要实现我们自己的功能,但更有意义的是:提出还是替代它们?如果有人发送无效数据,似乎必须将其视为错误?如果是这样的话:

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 

另外,你可以删除最后一个条款,它也失败了。

现在,我们可以把它放在一起:

decoded = Base.decode16!(string, case: :mixed) 
validate_ascii!(decoded) 
decoded 

编辑:如果你需要用点来代替非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 
+0

感谢这个解决方案!看到你解决问题的方法真的很有帮助。可悲的是,虽然我无法使用它,因为我正在致力于构建一个十六进制有效负载检查器,所以需要替换它,所以我可能/可能需要将字符显示为一个句点。有了这个说法,这对于看到另一种处理方法非常有帮助,而不需要明确的检查,而是通过模式匹配解决方案。谢谢!现在在https://howistart.org/posts/elixir/1 – kkirsche

+1

做你的教程很高兴帮助!我已经添加了一个关于如何用点替换非ascii的例子。 –

0

解决方案结束了如下,但如果有一个更清洁的或更好的解决方案,我会在明知很感兴趣。

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 
相关问题