Попытаться быть
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.
Рад, если вы внесете свой вклад в ★