2015-05-07 31 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 
相關問題