Бинарник

Бинарник — тип данных, а именно битстрока, длина которой делится на 8. Предназначен для хранения и передачи сырых, упакованных данных. То же: binary.

Бинарник предназначен для хранения и передачи (например, через сокеты) хорошо упакованных данных — так, чтобы они занимали как можно меньше места. Эрланг хорошо оптимизирует работу с бинарниками.

Там, где возможно, бинарники следует использовать для хранения неструктурированных данных, например больших строк или содержание какого-либо файла. В Эрланге эффективным является чтение файла сначала в бинарник — с последующим его разбором. И наоборот, при записи данных в файл их следует сначала превратить в бинарник, а бинарник уже и записать в файл.

Бинарник рассматривается как подмножество битстроки — в которой количество битов делится на 8. На практике битстрокой называют те случаи, когда она содержит количество битов, которое не делится на 8.

Литерал бинарника выглядит как последовательность целых чисел (0..255), заключённая в двойные угловые скобки:

1> <<1,10,100,200>>.
<<1,10,100,200>>

Как известно, в Эрланге строк как самостоятельного типа данных нет. Строковые литералы появляются в Системе просто как последовательность соответствующих чисел (согласно кодам).

2> <<1,"Yesterday.",3>>.
<<1,89,101,115,116,101,114,100,97,121,46,3>>

Если угодно, можно использовать числа больше или меньше диапазона 0..255. В Систему тогда попадут числа — целочисленный остаток от деления на 256.

3> <<256,257,258>>. 
<<0,1,2>>
4> <<-255,-2,-3>>.
<<1,254,253>>

Можно, конечно, использовать и кириллические буквы. Их коды так же превратятся в числа в диапазоне 0..255.

5> "Ну, погоди!".
[1053,1091,44,32,1087,1086,1075,1086,1076,1080,33]
6> <<"Ну, погоди!">>.
<<29,67,44,32,63,62,51,62,52,56,33>>

Когда с помощью знака равно привязываем бинарник к переменной, надо не забыть ставить пробелы вокруг =. Иначе будет ошибка, вызванная тем, что =< воспринимается как “равно или меньше”.

1> B1 = <<1>>. 
<<1>>
2> B2 =<<1>>.
* 2:6: syntax error before: '<'

Бинарники можно обрабатывать с помощью специальных функций или же с помощью бит-синтаксиса.

Обработка бинарников с помощью бифов

list_to_binary(L) -> B берёт иолист L и превращает его в бинарник.

1> L="hello". 
"hello"
2> list_to_binary(L). 
<<"hello">>

Иолист это не всякий список, а лишь тот, который состоит из элементов — чисел 0..255, бинарников, других иолистов. Если бы мы вместо “hello” написали “здравствуй”, то получили бы ошибку, потому что коды кириллического юникода заметно больше 255.

Иолист может быть весьма “мохнатым”, но list_to_binary уплощает его, превращая в простую последовательность целых чисел 0..255.

1> L1 = "hello". 
"hello"
2> L2 = [L1] ++ "you". 
["hello",121,111,117]
3> L3 = [L2] ++ "and you". 
[["hello",121,111,117],97,110,100,32,121,111,117]
4> list_to_binary(L3). 
<<"helloyouand you">>

Как уже говорилось, иолист может включать в себя бинарник. Он тоже займёт своё место в большом бинарнике.

L4 = [L3] ++ <<"and binarnik">>. 
[[["hello",121,111,117],97,110,100,32,121,111,117]|<<"and binarnik">>]
7> list_to_binary(L4). 
<<"helloyouand youand binarnik">>

split_binary(Bin, Pos) -> {Bin1, Bin2} берёт бинарник Bin и по позиции Pos режет его на две части. Возвращает кортеж, состоящий из этих двух бинарников.

1> B = <<1,2,3,4,5,6,7>>. 
<<1,2,3,4,5,6,7>>
2> split_binary(B,3).
{<<1,2,3>>,<<4,5,6,7>>}

term_to_binary(Term) -> Bin и binary_to_term(Bin) -> Term позволяют любой терм (например, атом или фунтерм) превратить в бинарник. Бинарник можно передать по сети, можно сохранить в файл, а потом прочитать его, извлечь бинарник, а из бинарника вытащить тот самый терм. Это отличная возможность, реализуется она очень просто.

Поскольку, например, фунтерм это тоже терм, его можно превратить в бинарник, передать через сеть на другой конец света, там извлечь и запустить — на тамошней Эрланг-системе.

1> F = fun(X) -> X*X end.
#Fun<erl_eval.42.105768164>
2> B = term_to_binary(F). 
<<131,112,0,0,3,89,1,201,188,156,143,16,126,173,32,90,79,
  205,206,160,193,177,248,0,0,0,42,0,0,...>>
3> F2 = binary_to_term(B). 
#Fun<erl_eval.42.105768164>
4> F2(11). 
121

byte_size(Bin) -> Size просто возвращает количество байтов в бинарнике.

1> byte_size(<<"привет">>).
6

Каждая буква из слова “привет” занимает, конечно, два байта. Однако сначала, как мы уже выяснили, код каждой буквы превращается в 0..255, поэтому данный бинарник и имеет размер 6 байтов.

Как соединить два бинарника?

Допустим, у нас есть бинарники <<1,2,3>> и <<4,5>>. Как получить бинарник <<1,2,3,4,5>> ? Для этого можно воспользоваться следующим способом:

1> B1 = <<1,2,3>>.
<<1,2,3>>
2> B2 = <<4,5>>.
<<4,5>>
3> <<B1/binary, B2/binary>>.
<<1,2,3,4,5>>

Бинарники и регулярные выражения

Для работы с бинарниками часто удобно использовать механизм регулярных выражений (модуль re). Например, с помощью регулярных выражений можно искать в бинарнике:

1> re:run(<<"To be, or not to be, that is the question.">>, "be").
{match,[{3,2}]}

Когда мы работаем с re:replace/4, то приходится задействовать бинарники, когда для замены используем фунтерм. Этот фунтерм должен принимать два аргумента: бинарник и список бинарников.

В опциях часто приходится ставить {return, list}, чтобы в результате получить строку, а не список из нескольких бинарников.

Обработка бинарников с помощью функций из модуля binary

at(Subject, Pos) -> byte() Возвращает байт (как целое число) из бинарника Subject по адресу Pos.

bin_to_list(Subject) -> [byte()] bin_to_list(Subject, PosLen) -> [byte()] bin_to_list(Subject, Pos, Len) -> [byte()] Превращают бинарник в последовательность байтов. В зависимости от арности функции меняется и её работа (принимаемые аргументы).

compile_pattern(Pattern) -> cp() Компилирует паттерн, который потом можно использовать в функциях match/3, matches/3, split/3, и replace/4.

copy(Subject) -> binary() copy(Subject, N) -> binary() Из бинарника Subject создаётся новый, в котором исходный бинарник будет повторяться 1 или N раз.

decode_hex(Bin) -> Bin2 Декодирует бинарник в 16-ричной кодировке.

decode_unsigned(Subject) -> Unsigned decode_unsigned(Subject, Endianness) -> Unsigned Превращают бинарник в одно большое беззнаковое число.

encode_hex(Bin) -> Bin2 encode_hex(Bin, Case) -> Bin2 Превращает бинарник в 16-ричный формат, например <<"C8FAF0">>.

encode_unsigned(Unsigned) -> binary() encode_unsigned(Unsigned, Endianness) -> binary() Превращает положительное число в бинарник.

first(Subject) -> byte() Возвращает первый байт.

last(Subject) -> byte() Возвращает последний байт.

list_to_bin(ByteList) -> binary() Последовательность байтов превращается в бинарник.

longest_common_prefix(Binaries) -> integer() >= 0 Сравнивает два бинарника и возвращает длину общего префикса.

longest_common_suffix(Binaries) -> integer() >= 0 Сравнивает два бинарника и возвращает длину общего суффикса.

match(Subject, Pattern) -> Found | nomatch match(Subject, Pattern, Options) -> Found | nomatch Ищет паттерн в бинарнике и возвращает найденную позицию и длину вхождения.

matches(Subject, Pattern) -> Found matches(Subject, Pattern, Options) -> Found Как match, но возвращает несколько результатов.

part(Subject, PosLen) -> binary() part(Subject, Pos, Len) -> binary() Извлекает часть бинарника.

referenced_byte_size(Binary) -> integer() >= 0 Если данный бинарник ссылается на больший бинарник, бывает полезно знать размер того бинарника.

replace(Subject, Pattern, Replacement) -> Result replace(Subject, Pattern, Replacement, Options) -> Result Создаёт новый бинарник путём изменения части старого.

split(Subject, Pattern) -> Parts split(Subject, Pattern, Options) -> Parts Режет бинарник на какое-то количество других бинарников.

Документация

Функции модуля binary.


© Алексей Карманов, 2024.