Как преобразовать двоичную копию эликсира в строку?
Итак, я пытаюсь преобразовать двоичный код в строку. Этот код:
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()
Предположения:
Так как я все еще изучаю эликсир, мне понадобилось некоторое время, чтобы найти это решение. Я просмотрел другие библиотеки, созданные людьми, даже используя что-то вроде 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>>