Треки
/
Elixir
Elixir
/
Вправи
/
Розпізнавач файлів
Розпізнавач файлів

Розпізнавач файлів

Навчальна вправа

Вступ

Бінарні дані

Elixir надає елегантний синтаксис для роботи з бінарними даними, як ми вже бачили на прикладі спеціальної форми <<>>, призначеної для роботи з бітовими рядками.

Тип «бінарні дані» є спеціалізацією типу «бітовий рядок». Бітові рядки можуть мати будь-яку довжину (будь-яку кількість бітів), а в бінарних даних кількість бітів ділиться на 8 без остачі. Тобто, працюючи з бінарними даними, ми часто думаємо про них у термінах байтів (8 бітів). Байт може представляти цілі числа від 0 до 255. Зазвичай значення байтів записують у шістнадцятковій системі, 0x00 - 0xFF.

Бінарні літерали визначають за допомогою спеціальної форми <<>>. Коли ми визначаємо бінарний літерал, можна використовувати цілі числа та рядки тексту (англ. string). Цілі числа, більші за 255, переповнюються, і використовуються лише останні 8 бітів числа. Типово до значення застосовується модифікатор ::binary. Ми можемо зʼєднувати бінарні дані оператором <>/2.

<<255>> == <<0xFF>>
# Overflowing bits are truncated
<<256>> == <<0>>
<<2, 4, 6, 8, 10, 12, 14, 16>> == <<0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10>>

Нульовий байт - це інша назва для <<0>>.

Зіставлення зі зразком для бінарних даних

Зіставлення зі зразком поширюється навіть на бінарні дані, і ми можемо зіставляти частину бінарних даних зі зразком так само, як робили б це з масивом.

# Ignore the first 8 bytes, match and bind the remaining to `body`
<<_::binary-size(8), body::binary>>

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

Вказівки

Ми працюємо над проєктом, який дозволяє користувачам завантажувати файли на сервер, щоб ділитися ними з іншими користувачами. Наше завдання - написати функцію, яка перевіряє, чи відповідає завантажений файл своєму типу медіа. Ми трохи досліджуємо це питання і зʼясовуємо, що перші кілька байтів файлу зазвичай унікальні саме для цього типу, що дає йому своєрідний підпис.

Скористаймося наведеною таблицею як довідкою:

Тип файлу Поширене розширення Тип медіа бінарний «підпис»
ELF "exe" "application/octet-stream" 0x7F, 0x45, 0x4C, 0x46
BMP "bmp" "image/bmp" 0x42, 0x4D
PNG "png" "image/png" 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A
JPG "jpg" "image/jpg" 0xFF, 0xD8, 0xFF
GIF "gif" "image/gif" 0x47, 0x49, 0x46

1. За заданим розширенням повернути очікуваний тип медіа

Реалізуймо функцію type_from_extension/1. Вона приймає розширення файлу, тобто рядок тексту (англ. string), і повертає тип медіа, теж рядок тексту, або nil, якщо розширення не відповідає очікуваним.

FileSniffer.type_from_extension("exe")
# => "application/octet-stream"

FileSniffer.type_from_extension("txt")
# => nil

2. За заданими бінарними даними файлу повернути очікуваний тип медіа

Реалізуймо функцію type_from_binary/1. Вона приймає файл (бінарні дані) і повертає тип медіа (рядок тексту) або nil, якщо підпис файлу не відповідає очікуваним.

file = File.read!("application.exe")
FileSniffer.type_from_binary(file)
# => "application/octet-stream"

file = File.read!("example.txt")
FileSniffer.type_from_binary(file)
# => nil

Не переймаймося читанням файлу як бінарних даних. Вважаймо, що це вже зроблено за нас і тести передають це як аргумент.

3. За заданими розширенням і бінарними даними файлу перевірити, чи відповідає файл очікуваному типу

Реалізуймо функцію verify/2. Вона приймає файл (бінарні дані) та розширення (рядок тексту) і повертає кортеж :ok або :error.

file = File.read!("application.exe")

FileSniffer.verify(file, "exe")
# => {:ok, "application/octet-stream"}

FileSniffer.verify(file, "png")
# => {:error, "Warning, file format and file extension do not match."}
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Elixir Exercism

Час розпочати Розпізнавач файлів?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Elixir, а також 58 концепцій168 вправ та справжнє наставництво від людей, і все це безкоштовно.