如何将Elixir二进制转换为字符串?

时间:2014-03-19 20:21:14

标签: unicode erlang odbc elixir

所以我试图将二进制文件转换为字符串。这段代码:

t = [{<<71,0,69,0,84,0>>}]
String.from_char_list(t)

但是当我尝试这种转换时,我得到了这个:

** (ArgumentError) argument error
    (stdlib) :unicode.characters_to_binary([{<<70, 0, 73, 0, 78, 0>>}])
    (elixir) lib/string.ex:1161: String.from_char_list/1

我假设&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; 70,0等可能是一个字素列表(它是API调用的返回,API没有完全记录)但我是否需要以某种方式指定编码?

我知道我可能会遗漏一些明显的东西(也许这不是正确的功能?)但我似乎无法弄明白该做什么。


编辑:

对于它的价值,上面的二进制文件是Erlang ODBC调用的返回值。经过一番挖掘后,我发现所讨论的二进制文件实际上是一个编码为UTF16小端的&#34; Unicode二进制文件&#34; (请参阅此处:http://www.erlang.org/doc/apps/odbc/odbc.pdf第9页re:SQL_WVARCHAR)确实没有改变问题,但它确实添加了一些上下文。

6 个答案:

答案 0 :(得分:19)

这里有几件事情:

1。)你有一个包含一个元素的元组列表,一个二进制文件。您可以只提取二进制文件并使用您的字符串。将当前数据结构传递给String.from_char_list是行不通的。

2。)您在示例中使用的二进制文件包含0,一个不可打印的字符。在shell中,由于Elixir无法区分二进制文件和表示字符串的二进制文件,而表示字符串的二进制文件包含不可打印的字符,因此不会将其正确打印为字符串。

3.。)您可以使用模式匹配将二进制转换为特定类型。例如:

iex> raw = <<71,32,69,32,84,32>> ...> Enum.join(for <<c::utf8 <- raw>>, do: <<c::utf8>>) "G E T " ...> <<c::utf8, _::binary>> = raw "G"

此外,如果您从网络连接获取二进制数据,您可能希望使用:erlang.iolist_to_binary,因为数据将是一个iolist,而不是charlist。不同之处在于,iolists可以包含二进制文件,嵌套列表,以及只是一个整数列表。查尔斯列表总是只是一个整数列表。如果你在iolist上拨打String.from_char_list,它就会失败。

答案 1 :(得分:3)

不确定OP是否已经解决了他的问题,但是关于他关于他的二进制文件utf16-le的评论:对于特别是编码,我发现最快(对于那些更有经验的Elixir,可能 - hacky)方式是使用Enum.reduce

raw = <<68, 0, 101, 0, 118, 0, 97, 0, 115, 0, 116, 0, 97, 0, 116, 0, 111, 0, 114, 0>>

# coercing it into utf8 gives us ["D", <<0>>, "e", <<0>>, "v", <<0>>, "a", <<0>>, "s", <<0>>, "t", <<0>>, "a", <<0>>, "t", <<0>>, "o", <<0>>, "r", <<0>>]
codepoints = String.codepoints(raw)

value = Enum.reduce(codepoints, "", fn(codepoint, result) ->
  << parsed :: 8>> = codepoint
  if parsed == 0, do: result, else: result <> <<parsed>>
end)

# "Devastator"
IO.puts(value)

假设:

  • utf16-le编码

  • 代码点向后兼容utf8,即它们只使用1个字节

由于我还在学习Elixir,我花了一些时间来解决这个问题。我查看了人们制作的其他图书馆,甚至在bash级别使用iconv之类的东西。

答案 2 :(得分:3)

我创建了一个将二进制转换为字符串

的函数
def raw_binary_to_string(raw) do
   codepoints = String.codepoints(raw)  
      val = Enum.reduce(codepoints, 
                        fn(w, result) ->  
                            cond do 
                                String.valid?(w) -> 
                                    result <> w 
                                true ->
                                    << parsed :: 8>> = w 
                                    result <>   << parsed :: utf8 >>
                            end
                        end)

  end

在iex控制台上执行

iex(6)>raw=<<65, 241, 111, 32, 100, 101, 32, 70, 97, 99, 116, 117, 114, 97, 99, 105, 111, 110, 32, 65, 99, 116, 117, 97, 108>>
iex(6)>raw_binary_to_string(raw)
iex(6)>"Año de Facturacion Actual"

答案 3 :(得分:1)

最后一点肯定确实更改了问题并解释了它。 Elixir使用二进制文件作为字符串,但假定并要求它们是UTF8编码,而不是UTF16。

答案 4 :(得分:1)

关于http://erlang.org/pipermail/erlang-questions/2010-December/054885.html

您也可以使用:unicode.characters_to_list(binary_string, {:utf16, :little})来验证结果并存储

IEX评估

iex(1)> y                                                
<<115, 0, 121, 0, 115, 0>>
iex(2)> :unicode.characters_to_list(y, {:utf16, :little})
'sys'

注意:将值sys打印为<<115, 0, 121, 0, 115, 0>>

答案 5 :(得分:1)

您可以使用理解力

    defmodule TestModule do
      def convert(binary) do
        for c <- binary, into: "", do: <<c>>
      end
    end
    TestModule.convert([71,32,69,32,84,32]) |> IO.puts