erlang 如何转换一个长生不老药二进制字符串?

im9ewurl  于 2022-12-08  发布在  Erlang
关注(0)|答案(7)|浏览(140)

所以我试着把一个二进制转换成一个字符串。这段代码:

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

我假设〈〈70,0等可能是一个字形列表(它是一个API调用的返回,该API没有详细说明),但是我需要以某种方式指定编码吗?
我知道我可能错过了一些明显的东西(也许这不是使用的正确函数?),但我似乎不知道在这里做什么。
编辑:
值得一提的是,上面的二进制代码是Erlang ODBC调用的返回值。经过进一步的挖掘,我发现这个二进制代码实际上是一个"编码为UTF16小端字节序的Unicode二进制代码"(请参见此处:http://www.erlang.org/doc/apps/odbc/odbc.pdf第9页回复:SQL_WVARCHAR)并没有真正改变问题,但它确实增加了一些上下文。

qv7cva1a

qv7cva1a1#

这里有几件事:
1.)你有一个列表,其中的元组包含一个元素,一个binary。你可以直接提取binary,得到你的字符串。把当前的数据结构传递给to_string是行不通的。
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。不同之处在于iolist可以包含二进制文件、嵌套列表,也可以只是整数列表。charlist始终只是整数的平面列表。如果在iolist上调用to_string,则会失败。

kq4fsx7k

kq4fsx7k2#

我做了一个函数把二进制转换成字符串

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"
8ehkhllq

8ehkhllq3#

不确定OP是否已经解决了他的问题,但是关于他关于他的二元系是utf16-le的评论:对于具体编码,我发现最快的(对于那些使用Elixir更有经验的人来说,可能是一种黑客)方法是使用Enum.reduce

# 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>>]
<<68, 0, 101, 0, 118, 0, 97, 0, 115, 0, 116, 0, 97, 0, 116, 0, 111, 0, 114, 0>>  
|> String.codepoints()
|> Enum.reduce("", fn(codepoint, result) ->
                     << parsed :: 8>> = codepoint
                     if parsed == 0, do: result, else: result <> <<parsed>>
                   end)

# "Devastator"
|> IO.puts()

假设条件:

  • utf16-le编码
  • 码点与utf8向后兼容,即它们仅使用1字节,

因为我还在学习Elixir,所以我花了一段时间才找到这个解决方案。我查看了其他人创建的库,甚至在bash级别使用了iconv这样的库。

fcipmucu

fcipmucu4#

Ecto.UUID.load/1将二进制转换为字符串并返回元组:

binary = Ecto.UUID.bingenerate()
<<99, 148, 189, 126, 144, 154, 71, 236, 160, 110, 149, 143, 67, 162, 177, 192>>

Ecto.UUID.load(binary)
{:ok, "6394bd7e-909a-47ec-a06e-958f43a2b1c0"}

贷方:https://stackoverflow.com/a/43530427/2091331

ttygqcqt

ttygqcqt5#

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

okxuctiv

okxuctiv6#

参照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'

<<115, 0, 121, 0, 115, 0>>的值打印为sys

ev7lccsx

ev7lccsx7#

你可以使用领悟

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

相关问题