Попытаться быть

Elixir Pro - Освоение двоичных файлов и битовых данных :)

Просто поиграйте со струнами и настройте их.

В этой статье рассказывается, как строки, двоичные файлы и битовые данные отличаются друг от друга. Это также дает вам представление о строках и необработанных байтах, объясняя с примерами в реальном времени. Я разработал изображения, используемые в этой статье, чтобы четко сфокусироваться на двоичных файлах. Надеюсь, они вам нравятся.

Версия Эликсира

Все примеры, использованные в этой статье, выполняются iex с использованием следующей комбинации Elixir/Erlang OTP.

Нежное вступление

Мне пришлось проделать тяжелую тренировку по синтаксическому анализу пакетов, используя длину заголовков при декодировании необработанных двоичных файлов и кодировании 16-, 32-, 64-битных строк в одном из моих проектов. Итак, у меня возникла мысль поделиться опытом.

Надеюсь, вы уже знали разницу между bitstring, binary, bit и byte. Если это правда, выполните: skip the following screen shot иначе: have a glance of it.

«Каждый binary - это bitstring , но каждый bitstring не должен быть binary»

В эликсире двоичный файл представлен как <<>>. Конечно, все знают.

iex(8) data = <<"hello">>
"hello"
iex(9) is_binary data
true
iex(10) is_bitstring data
true
iex(11) data2 = <<1,2,3::4>>
<<1, 2, 3::size(4)>>
iex(12) is_bitstring data2
true
iex(13) is_binary data2
false

Что отличает двоичный файл от строки битов?

Если число bits кратно 8, мы называем его binary.

Рассмотрим следующий пример.

<<1,2,3::4>>

В приведенной выше строке мы не упомянули число bits , которое будет использоваться для 1,2, но мы представили 3. В elixir, если размер не указан, используется значение по умолчанию 8 bits. Итак, <<1,2,3::4>> равно <<1::8, 2::8, 3::4>>, что является данными 20 bit. Мы не можем назвать это числом binary , поскольку число bits is 20 , которое не кратно 8.

Взгляните на следующее представление.

Необработанные байты и представление об эликсире

Строки в эликсире - это двоичные файлы. Извините, что повторяю одно и то же утверждение снова и снова. Но я должен сделать. Даже когда вас просят проснуться, вы должны это сказать.

Рассмотрим слово hello каждая буква или grapheme займет 8 бит. Итак, общее byte_size слова hello составляет 5.

iex> byte_size "hello"
5
iex> String.graphemes "hello"
["h", "e", "l", "l", "o"]
iex> String.valid? <<35>>
true
iex> <<35>>
"#" // valid string

Код ASCII (Американский стандартный код для обмена информацией) для # - 35. Двоичное представление 35 - это 100011 6-битные данные.

Здесь << 35 >> означает, что мы говорим использовать 8 бит для 35. Итак, 00100011 - это двоичная форма для 35. Если вы представляете, что << 35::6>> попадает под raw bytes данных.

iex> <<35::6>>
<<35::size(6)>>
iex> String.valid?(<<35::6>>)
false
iex> String.valid?(<<35::8>>)
true

Понимание представления эликсира

Рассмотрим следующие строки кода

iex> match?("#", <<35>>)
true
iex> match? "#", <<0::1, 0::1, 1::1, 0::1, 0::1, 0::1, 1::1, 1::1>>
true 
iex> match? <<35>>, <<0::1, 0::1, 1::1, 0::1, 0::1, 0::1,1::1,1::1>>
true

Здесь мы буквально делим каждый бит с << 35::8 >> на <<0::1, 0::1, 1::1, 0::1, 0::1, 0::1,1::1, 1::1>>.

Бэкэнд история обучения

Когда я изучал основы программирования на Эликсире, я обычно переворачивал страницы, не читая, когда я когда-либо видел символы <<>>. Эти символы казались кошмаром, когда я был ребенком по сравнению с Эликсиром. Их изучение похоже на ощущение удара головой в гору на скорости 200. Только представьте.

OK! Истории расходятся. Но как только вы получите четкое представление о том, что означают необработанные байты и действительные строки в своем уме, вы подниметесь на гору с простота.

Программисты больше имеют дело с необработанными байтами в своей жизни, чем с строками. Особенно тот, кто всегда занимается парсингом.

Программисты учитывают память, но не длину.

Помните предыдущую строчку, мы поговорим об этом позже в статье более подробно.

Извлечение подстроки

Это реальная ситуация.

Извлечение строки известной длины

Если вы знаете точную длину строки и позицию, откуда вы хотите извлечь, вы можете использовать следующий подход

Использование binary_part для сырых байтов

Когда вы останавливаетесь на реальном проекте, вы сталкиваетесь с ситуациями, связанными с необработанными байтами данных. Я бы посоветовал вам узнать как можно больше, прежде чем работать с необработанными байтами данных.

iex> binary_part("hello medium", 6, 6)
"medium"

binary_part(binary, start, length) извлекает двоичную часть от start до length. Он используется для разделения необработанных байтов данных.

Если длина отрицательна и находится в пределах границ, строка извлекается из справа налево, в отличие от слева направо .

То, что нужно запомнить.

→ Здесь индекс не может быть отрицательным.

→ Здесь двоичные файлы zero-indexed означают, что binary_part("hello",1,1) приведет к e, а не h. Вы должны попробовать binary_part("hello", 0,1). Надеюсь, вы поняли, что такое нулевой индекс.

start и length не могут превышать строку byte_size of. В противном случае возникает Argument Error исключение.

Использование binary_part в предложении Guard

Это определение также можно использовать в защитной оговорке.

Пример: анализ пакетов

Например, вы анализируете такие пакеты, как $admin#medium#worlds#best#blog, $user#blackode#a#medium#writer. Вас просят написать определение, которое получает пакет, и вы должны отличать каждый пакет от другого.

Вы можете сделать это, разделив пакет, например, String.split(packet, "#"), и используя макрос if для выполнения работы. Но для этого требуется больше логики кода. Вы можете использовать binary_part в защитном предложении, как показано ниже.

defmodule Parser do
  def parse(packet) when binary_part(packet, 1,5)=="admin" do
    IO.puts "Admin Packet !"
  end
  ...
end

Посмотрите скриншот выполнения

================= Предупреждение =================

Как я уже упоминал в разделе о том, что следует помнить, если значения length или start выходят за границы, возникает исключение Argument Error.

- Извлечение строки неизвестной длины

Если вы не знаете длину подстроки, вы не можете использовать функцию binary_part. А вот и двоичное сопоставление шаблонов <<>> in удобно.

Ситуация
Вам предлагается извлечь строку с позиции 6 до конца в строке.

Строка в Эликсире кратна 8 битам, которые мы называем двоичной. Это означает, что если bit_size делится на 8, мы называем это bitstring as binary.

Как мы говорили ранее во вводном разделе, каждая буква в строке означает 8 bits означает 1 byte. Итак, чтобы пропустить 6 букву, вам нужно пропустить 6x8 бит.

- Извлечение первой буквы из строки

Ситуация
Извлеките символ валюты из строки «$ 500».

Этого можно добиться разными способами.

String.first

iex> string = "$500"
"$500"
iex> string |> String.first
"$"

Соответствие шаблону

iex> string = "$500"
"$500"
iex> <<first::8,_rest::binary>> = string
"$500"
iex> <<first>>
"$"
iex> first
36            // code_point ascii-code of $
iex> <<35>>
"#"

String.split

Не рекомендуется в этой ситуации, но полезно знать о существовании этой опции.

Как мы знаем, он разбивает строку по заданному шаблону. Если шаблон "", он дает другой результат.

iex> string = "$500"
"$500"
iex> string |> String.split("")
["", "$", "5", "0", "0", ""]

примечание: между "" нет пробелов

Если вы обратите внимание, он добавил несколько дополнительных "" в голове и хвосте. Вам нужно снова обрезать их, передав параметр trim: true.

iex> string = "$500"
"$500"
iex> string |> String.split("", trim: true) |> hd
"$"

String.slice

iex> String.slice "$500", 0, 1
"$"
iex> String.slice "$500", -4, 1
"$"

String.slice [VS] binary_part

Как мы знаем, оба будут принимать аргументы как (str, start, len) и возвращать подстроку, начинающуюся со смещения start, и длиной len.

Я все думал, зачем нужны две функции с похожей функциональностью. Итак, я начал проверять то, что их отличает.

Параметры вне пределов

Когда start и len выходят за границы, binary_part вызовет ошибку аргумента, поскольку он предназначен для использования вместе с необработанными байтами, но не String.slice, который относится к String.length.

Давай проверим.

iex(14) str = "hello medium" 
"hello medium"
iex(15) String.slice str, 6, 10
"medium"
iex(16) binary_part str, 6, 10
Bug Bug ..!!** (ArgumentError) argument error
    :erlang.binary_part("hello medium", 6, 10)

Здесь после позиции 6 остались только буквы 6, но мы попытались извлечь подстроку из len 10. Итак, binary_part вызвал ошибку, но не String.slice, которая дала результат подстроки от индекса 6 до конца строки. Надеюсь, вы поняли.

Необработанные байты и графемы

Функция String.slice(str, start, len), start - это индекс графем, тогда как в binary_part это индекс байта. .

Это будет более понятно на следующем примере.

iex> str = "hełło" 
"hełło"
iex> String.length str
5
iex> byte_size str
7
iex> String.graphemes str
["h", "e", "ł", "ł", "o"]

Надеюсь, вы понимаете, что я имею в виду под graphemes. graphemes длина str - это 5 , но его byte_size это 7 , вот чем эти функции отличаются друг от друга.

byte_size/1 считает лежащие в основе raw bytes, а String.length/1 считает characters.

Функция String.slice работает с графемами Unicode, а binary_part deals byte_size.

В общем, binary_part имеет дело с raw bytes.

Внутреннее представление строки (необработанные байты)

Вы можете увидеть binary представление любой строки с небольшим приемом, соединяющим строку с <<0>>.

iex> str = "hełło" 
"hełło"
iex> raw = str <> <<0>>
<<104, 101, 197, 130, 197, 130, 111, 0>>
iex(37) String.slice raw, 2, 3
"łło"
iex(38) binary_part raw, 2, 3 
<<197, 130, 197>>

В эликсире есть модуль Base, который помогает вам в декодировании и кодировании двоичных файлов. Посмотрите здесь.

Надеюсь, вам понравилось играть на струнных. Практика делает вас более совершенными. Попробуйте проанализировать пакет ipv4 на основе его длины заголовка.

Если вы найдете это полезным, пожалуйста, протяните руку, чтобы поделиться. Пусть другие извлекут из этого пользу.

Удачного кодирования! Всегда улыбайся :)

Присоединяйтесь к нашему каналу Telegram



«Blackoders
EAT 🍕 - CODE🐞 - SLEEP😴 Код, мысли и идеи Ресурсы по кодированию, советы, видео, статьи и новости Мы следим и собираем… t .меня"



Посетите репозиторий GitHub в разделе Советы по Killer Elixir.

Рад, если вы внесете свой вклад в ★