Как преобразовать двоичную копию эликсира в строку?

Итак, я пытаюсь преобразовать двоичный код в строку. Этот код:

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 не является документированным), но мне нужно каким-то образом указать кодировку?

Я знаю, что, скорее всего, у меня нет чего-то очевидного (может быть, это не правильная функция для использования?), но я не могу понять, что делать здесь.


EDIT:

Для чего это стоит, двоичный код выше - это возвращаемое значение вызова Erdang ODBC. После немного большего копания я обнаружил, что рассматриваемый двоичный код на самом деле является "двоичным кодом Unicode, закодированным как UTF16 little endian" (см. Здесь: http://www.erlang.org/doc/apps/odbc/odbc.pdf стр. 9 re: SQL_WVARCHAR) На самом деле это не изменяет проблему, но добавляет некоторый контекст.

Ответ 1

Здесь есть пара вещей:

1.) У вас есть список с кортежем, содержащим один элемент, двоичный файл. Вы можете, вероятно, просто извлечь двоичный файл и получить вашу строку. Передача текущей структуры данных to_string не будет работать.

2.) Бинарный файл, который вы использовали в своем примере, содержит 0, непечатаемый символ. В оболочке это не будет печататься должным образом в виде строки, потому что 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 могут содержать двоичные файлы, вложенные списки, а также просто быть списком целых чисел. Чарлисты - это всегда просто список целых чисел. Если вы to_string для iolist, это не удастся.

Ответ 2

Не уверен, что с тех пор 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 байт

Так как я все еще изучаю эликсир, мне понадобилось некоторое время, чтобы найти это решение. Я просмотрел другие библиотеки, созданные людьми, даже используя что-то вроде iconv на уровне bash.

Ответ 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"

Ответ 4

Последняя точка, определенная , делает изменение проблемы и объясняет ее. Elixir использует двоичные файлы как строки, но предполагает и требует, чтобы они кодировались UTF8, а не UTF16.

Ответ 5

Ссылка на http://erlang.org/pipermail/erlang-questions/2010-De December/054885.html

Вы можете использовать :unicode.characters_to_list(binary_string, {:utf16, :little}) чтобы проверить результат и сохранить тоже

IEX eval

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>>