До списку уроків
8A · Урок 11 з 16

Формат — це структура, а не розширення (magic numbers)

виконано0%
Крок 1

Що робимо сьогодні

Модуль I · Тема 6, урок 1 із 2 · 120 хвилин

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

Після уроку ти вмієш

Слова «сигнатура», magic number і «MIME-тип» поки нічого не означають — розберемо їх по черзі.

Хід уроку

ЕтапХвЩо робимо
Фокус із перейменуванням10Чому браузер не обманувся
Сигнатури й hex25Перші байти десяти форматів
Hex-детектив15Вісім файлів без імені — упізнаємо формат за байтами
Перерва10
Офсети й контейнери15Чому MP4 і DOCX — окремий випадок
MIME і Content-Type20Як тип передається по мережі
Інструменти в терміналі15file, xxd, Format-Hex
Практика й підсумок10Розбір набору й detect.txt
Разом120
Як рахується оцінка Внизу сторінки — шкала виконання: кроки практики, автоперевірка на сервері, відповіді на питання й очна перевірка. Відсоток видно в шапці постійно.
Крок 2

Перейменувати файл — не означає змінити файл

«Розширення = тип файлу». Це неправда, і сьогодні ти в цьому переконаєшся

Візьми будь-яку фотографію, перейменуй photo.png на photo.jpg — і подивись на розмір файлу. Він не змінився. Жоден байт усередині не поворухнувся. Змінився тільки напис в імені.

Ім'я файлу зберігається у файловій системі, поруч із файлом, а не всередині нього. Це підпис на коробці. Вміст коробки він не змінює.

обидва файли називаються котик.jpg ім'я у файловій системі ім'я у файловій системі котик.jpg котик.jpg перші байти всередині перші байти всередині FF D8 FF E0 89 50 4E 47 справді JPEG · image/jpeg насправді PNG · image/png Провідник дивиться на ім'я — бачить два однакові файли. Команда file дивиться всередину — бачить два різні формати.
Однакове ім'я нічого не гарантує. Різницю видно тільки в перших байтах — і саме на них дивляться всі серйозні програми.
Питання

Ти перейменував схема.png на схема.jpg, щоб «зменшити вагу». Що змінилося всередині файлу?

Але тоді виникає питання. Ім'я нічого не означає — як браузер розуміє, що йому надіслали картинку, а не текст? Відповідь проста: кожен формат починається з упізнаваного підпису. Його називають сигнатурою файлу або magic number — «чарівним числом».

Практичний наслідок Перейменування — це не конвертація. Якщо надіслати в друкарню logo.png, перейменований на logo.pdf, друкарня отримає не PDF, а зіпсоване ім'я. Конвертація змінює структуру всередині, перейменування — лише напис зовні.
Питання

Тобі надіслали котик.jpg, який не відкривається у «Фотографіях». Ти запускаєш file --mime-type котик.jpg, і він каже image/png. Що сталося і як полагодити?

Крок 3

Перші байти: сигнатура файлу

Кілька байтів на початку, за якими формат упізнають однозначно

Файл — це послідовність байтів. Кожен байт — це число від 0 до 255. Записувати його прийнято двома шістнадцятковими цифрами: 00, 0A, FF. Такий запис називають hex.

Автори форматів домовилися так. На самому початку файлу стоїть кілька байтів, які більше ніде не зустрічаються в такому порядку. Прочитав перші 4–8 байтів — і вже знаєш формат. Решту гігабайта читати не треба.

Як це виглядає на PNG

офсет 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 як текст · P N G · · · · · · · · I H D R сигнатура PNG — 8 байтів довжина блоку: 13 тип блоку: IHDR блок IHDR несе ширину, висоту й глибину кольору — саме звідти їх бере переглядач
Перші 16 байтів реального PNG. Літери PNG видно навіть у текстовому поданні — це зроблено навмисно, щоб людина впізнавала формат очима.

Чому в PNG аж вісім байтів, а не три

Кожен байт сигнатури PNG ловить свою поломку при передаванні файлу:

БайтНавіщо він там
89число більше 127 — якщо канал зв'язку «обрізає» восьмий біт, байт зіпсується й це одразу помітно
50 4E 47літери PNG — щоб формат упізнавала людина
0D 0Aпара «повернення каретки + переведення рядка»: ловить передавання в текстовому режимі, яке псує кінці рядків
1Aу DOS це «кінець файлу» — команда type зупиниться тут і не заллє екран сміттям
0Aще одне переведення рядка — ловить зворотне перетворення

Це не випадковий набір: так написано в самій специфікації PNG. Формат захищається від тихого псування при пересиланні. Щоб зіпсований файл не прикинувся цілим.

Десять сигнатур, які варто знати

Учити їх напам'ять не треба. Але саме ці формати трапляються щодня, і за тиждень практики ти впізнаватимеш їх з одного погляду. Колонка «Офсет» каже, з якого байта дивитися: нуль — від самого початку файлу.

ФорматБайти (hex)ОфсетЯк текст
PNG89 50 4E 47 0D 0A 1A 0A0·PNG····
JPEGFF D8 FF0
GIF47 49 46 380GIF8
PDF25 50 44 460%PDF
ZIP (і DOCX, XLSX, ODT, EPUB, APK)50 4B 03 040PK··
MP3 із тегом49 44 330ID3
MP3 без тегаFF FB0
WAV52 49 46 46 … 57 41 56 450 і 8RIFF…WAVE
MP466 74 79 704ftyp
ELF (програма Linux)7F 45 4C 460·ELF

MP3 без тега починається також із FF F3, FF FA або FF F2. Спільне в них — перший байт FF. Ним починається кожна порція звуку всередині MP3, а другий байт уточнює версію формату — тому він і різний.

Питання

Однокласник надіслав звіт.jpg. Ти дивишся перші байти — там 50 4B 03 04. Що це насправді?

Крок 4

Hex-детектив

Вісім файлів без імені. Усе, що ти бачиш, — перші 16 байтів

Нижче — початок справжнього файлу. Угорі номер байта (офсет), посередині сам байт у hex, знизу той самий байт як текст. Крапкою позначено байт, який не має друкованого символу.

Твоє завдання — упізнати формат. Після відповіді сигнатура підсвітиться зеленим, і ти побачиш, що саме тебе мало наштовхнути.

Раунд 1 з 8 Правильно 0

рядок 1 — офсет · рядок 2 — байт у hex · рядок 3 — той самий байт як текст

Що це за файл?

Що тут головне Жодного разу ти не бачив ані імені файлу, ані розширення — і все одно визначив формат. Саме так працює команда file і будь-який пристойний переглядач зображень.
Питання

Два файли починаються однаково: 52 49 46 46 (RIFF). Один із них WAV, другий — WebP-картинка. Як їх розрізнити?

Крок 5

Чому MP4 не має підпису на початку

Формати-контейнери влаштовані інакше: сигнатура зсунута

Відкрий MP4 у hex-переглядачі — і на нульовому байті не буде нічого схожого на підпис. Там стоятиме щось на кшталт 00 00 00 20. Це не помилка.

MP4 зібраний із боксів (їх також називають атомами). Кожен бокс починається однаково. Спершу чотири байти — розмір боксу. Далі ще чотири — його чотирилітерна назва. Перший бокс у файлі завжди ftyp — «тип файлу». Отже, розмір з'їдає офсети 0–3, а літери ftyp починаються з офсету 4.

офсет 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 00 00 20 66 74 79 70 69 73 6F 6D 00 00 02 00 як текст · · · f t y p i s o m · · · · розмір боксу: 0x20 = 32 ftyp ← сигнатура бренд: isom той самий устрій мають MOV, M4A і 3GP — це все один сімейний формат ISO BMFF
Сигнатура MP4 стоїть на офсеті 4, бо перші чотири байти зайняті розміром першого боксу. Тому file шукає підпис не лише на початку файлу, а й у заздалегідь відомих місцях.
Питання

Ти написав власний скрипт, який читає рівно перші 4 байти файлу й порівнює їх зі списком сигнатур. На всіх MP4 скрипт мовчить. Чому?

DOCX, XLSX і ODT — це ZIP-архіви

Відкрий будь-який .docx у hex-переглядачі: там буде 50 4B 03 04, тобто PK··. Це підпис ZIP. Літери PK — ініціали Філа Каца, автора формату.

Сучасний офісний документ — це не один шматок даних. Це папка з файлами, запакована в архів. Текст лежить в одному XML, стилі в іншому, картинки окремими файлами. Це стандарт: OOXML для Microsoft Office, ODF для LibreOffice.

Перевірити це можна не відкриваючи Word. Команда unzip -l друкує список того, що лежить в архіві. А хвостик | head -8 просить показати тільки перші вісім рядків. Без нього список поїхав би на цілий екран, бо файлів усередині документа десятки.

перевіряємо, що docx — це архів
$ unzip -l реферат.docx | head -8 Archive: реферат.docx Length Date Time Name --------- ---------- ----- ---- 1751 1980-01-01 00:00 [Content_Types].xml 590 1980-01-01 00:00 _rels/.rels 16334 1980-01-01 00:00 word/document.xml 817 1980-01-01 00:00 word/_rels/document.xml.rels 2210 1980-01-01 00:00 word/styles.xml

Ось із чого складається твій реферат. У word/document.xml лежить сам текст, у word/styles.xml — оформлення, а [Content_Types].xml перелічує, що взагалі є в архіві. Word просто пакує ці файли разом і показує тобі як один документ.

Вивід ілюстративний — у твоєму файлі будуть інші розміри. Незмінне тут інше: [Content_Types].xml і word/document.xml є в кожному DOCX. В ODT замість них перший запис завжди називається mimetype і містить рядок application/vnd.oasis.opendocument.text.

Спробуй удома Скопіюй будь-який .docx, перейменуй копію на .zip і розпакуй звичайним архіватором. Ти побачиш усередині теку word/ з файлом document.xml — там і лежить твій текст. Оригінал при цьому не чіпай.
Уточнення, щоб не переплутати За перші чотири байти 50 4B 03 04 DOCX неможливо відрізнити від звичайного ZIP чи від EPUB. Формат уточнюють вміст архіву й розширення. Тому file для DOCX часто відповідає обережно — application/zip, а іноді точніше: application/vnd.openxmlformats-officedocument.wordprocessingml.document.
Крок 6

MIME-тип: як сервер називає тип файлу

Ім'я файлу по мережі не передається. Передається тип

Досі ми дивилися у файл, який лежить у тебе на диску. Але той самий файл ще й їздить по мережі. У дорозі імені в нього немає взагалі — то за чим його впізнає браузер?

Коли браузер запитує файл, сервер відповідає заголовком Content-Type. Саме він каже, що робити з тілом відповіді. Значення записують у форматі тип/підтип: image/png, text/html, application/json, audio/mpeg. Це і є MIME-тип.

сервер знайшов файл на диску HTTP-відповідь HTTP/1.1 200 OK Content-Type: image/png Content-Length: 48231 браузер читає заголовок і вирішує, що робити image/png показує картинку text/plain показує як текст application/octet-stream пропонує завантажити той самий файл, три різні заголовки — і три різні реакції браузера
Розширення в адресі браузер узагалі не зобов'язаний читати. Рішення ухвалюється за Content-Type.

Звідси беруться дві щоденні дивини. Перша — про JSON. Якщо сервер віддав application/json або text/plain, файл відкривається в браузері як текст. А якщо application/octet-stream — браузер його завантажує.

Друга: картинка не показується, хоча за адресою вона є. Майже завжди сервер віддав її з типом text/plain. Так буває, коли файл лежить без розширення й веб-сервер не зміг здогадатися.

ФайлMIME-типЩо робить браузер
index.htmltext/htmlмалює сторінку
photo.pngimage/pngпоказує зображення
report.csvtext/csvздебільшого пропонує завантажити
manifest.jsonapplication/jsonпоказує як текст
track.mp3audio/mpegвмикає вбудований плеєр
archive.zipapplication/zipзавантажує

Подивись на report.csv у таблиці. Малювати таблиці браузер не вміє, тому чесно віддає файл тобі на завантаження. А track.mp3 він відкриє прямо у вкладці — бо для audio/mpeg у нього є плеєр.

Зверни увагу: MP3 має тип audio/mpeg, а не audio/mp3. MIME-типи не вигадують — їх реєструє організація IANA, і в звіті треба писати саме зареєстровану назву.

Питання

Ти виклав на сервер manifest.json. У браузері він відкривається нормально, а от чужий скрипт, який його читає, скаржиться на тип. Що треба перевірити першим?

Крок 7

Три команди, які дивляться всередину

file називає формат, xxd і hexdump показують самі байти

Команда file має власну базу сигнатур (її називають magic). Вона читає початок файлу, шукає збіг у базі й повертає назву формату. Ім'я файлу вона при цьому не враховує взагалі.

за іменем котик.jpg файл на диску бере текст після крапки і шукає його в таблиці програм «це JPEG» і помиляється за вмістом котик.jpg той самий файл читає перші байти: 89 50 4E 47 і звіряє їх з базою сигнатур magic image/png і має рацію одні й ті самі байти на диску — два різні висновки, бо дивилися в різні місця
Провідник Windows і команда file відповідають на різні запитання. Перший — «як цей файл назвали», друга — «що в ньому лежить».

file — головна команда уроку

Стань у теку з файлами й запусти file одразу на всіх. Зірочка означає «кожен файл у цій теці». Команда відкриє їх по черзі, зазирне в перші байти й напише, що там насправді лежить.

справжній тип усіх файлів у теці
$ file --mime-type * котик.jpg: image/png нотатки.txt: image/png пісня.pdf: audio/mpeg схема.svg: image/svg+xml звіт.docx: application/zip архів.zip: application/zip

Три перші рядки — це і є «спіймані»: тип не збігається з розширенням. Ключ --mime-type дає коротку відповідь у форматі тип/підтип. Саме її треба записувати у звіт.

Без ключа file відповідає людською мовою і значно докладніше:

докладний опис замість MIME
$ file котик.jpg котик.jpg: PNG image data, 800 x 600, 8-bit/color RGBA, non-interlaced

Ширину й висоту file дістав із блока IHDR — того самого, який ми бачили на схемі перших 16 байтів.

xxd — подивитися байти власними очима

перший рядок дампа: 16 байтів
$ xxd котик.jpg | head -1 00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR

Читається так. Ліворуч офсет — номер першого байта в рядку. Посередині 16 байтів парами. Праворуч ті самі байти як текст. Крапка означає байт без друкованого символу. Слово PNG видно неозброєним оком.

Обов'язково з | head Без нього xxd висипле в термінал увесь файл: для 3-мегабайтної фотографії це близько 190 тисяч рядків. Термінал не зависне назавжди, але прокручуватиме сміття довго. Правило: завжди | head -1 або | head -2.

hexdump — той самий вигляд, інша команда

канонічний формат hexdump
$ hexdump -C пісня.pdf | head -2 00000000 49 44 33 03 00 00 00 00 21 76 54 49 54 32 00 00 |ID3.....!vTIT2..| 00000010 00 0b 00 00 03 d0 9f d1 80 d0 b8 d0 b2 d1 96 d1 |................|

Ключ -C вмикає «канонічний» вигляд: байти окремо, текст у вертикальних рисках. Файл називається пісня.pdf, а всередині ID3 — тег MP3. Розширення бреше.

А що на Windows

У «чистому» PowerShell немає ані file, ані xxd. Вони приходять із Git Bash або WSL. Але подивитися байти PowerShell уміє сам:

powershell: перші рядки дампа
$ Format-Hex -Path .\котик.jpg | Select-Object -First 3 Label: C:\media\lesson-06\котик.jpg 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00000000 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 .PNG........IHDR

У PowerShell команди head немає. Її роль тут грає Select-Object -First 3 — він лишає тільки три перші рядки. Без нього PowerShell так само висипле у вікно весь файл.

Отже, три команди відповідають на одне питання по-різному. file каже назву формату словами. xxd і hexdump показують самі байти, а висновок ти робиш очима. Ось те саме коротко, для двох систем.

ЗадачаGit Bash / macOS / LinuxPowerShell 5.1
Справжній тип файлуfile --mime-type fнемає — став Git Bash
Перші 16 байтівxxd f | head -1Format-Hex -Path f | Select-Object -First 3
Перші 32 байтиhexdump -C f | head -2Format-Hex -Path f | Select-Object -First 4
Зберегти вивід у файлfile --mime-type * > detect.txt… | Out-File -Encoding utf8 detect.txt
Пастка PowerShell, через яку падає чекер У Windows PowerShell 5.1 оператор > записує файл у кодуванні UTF-16. Чекер читає detect.txt як UTF-8 і бачить сміття замість твоїх рядків. Тому в PowerShell зберігай тільки через | Out-File -Encoding utf8 detect.txt. У Git Bash, macOS і Linux звичайне > працює правильно.
Питання

Ти набрав xxd відео.mp4 без нічого — і термінал уже хвилину сипле рядками. Що треба було написати?

Питання

У detect.txt ти написав рядок нотатки.txt | txt | png | збіг: ні, а чекер дає червоне. Що не так із рядком?

Крок 8

Практика: десять файлів, три з чужим розширенням

Уся робота — у своєму терміналі, на своєму комп'ютері. На сервер поїде тільки текст. Галочки зберігаються.

Чому тека зветься lesson-06, якщо урок 11 Теки практики звуться за темою, а не за номером уроку. Це вже шоста тема модуля, тому тека — media/lesson-06/, а на сервері ~/www/lesson-06/. Не шукай lesson-11, її немає.

Формат рядка в detect.txt

десять рядків, по одному на файл
котик.jpg | jpg | image/png | збіг: ні схема.svg | svg | image/svg+xml | збіг: так пісня.pdf | pdf | audio/mpeg | збіг: ні

Третє поле — завжди MIME-тип у форматі тип/підтип, а не розширення. image/png — правильно, png — червоний пункт у чекері.

Що нікуди не завантажується Розпакований набір mix лишається в локальній теці media/lesson-06/. Там є .mp3, .jpg, .png і сам mix.zip — FTP-приймальня відхиляє їх автоматично. На сервер їдуть рівно три текстові файли: index.html, detect.txt, signatures.md.
Крок 9

Автоперевірка

Чекер знає правильні відповіді для набору наперед — самих файлів на сервері немає. Спроби не обмежені.

Демонстраційний режим: результат згенеровано для показу. На сервері ця кнопка викликає POST /api/check, який запускає check 06 від імені учня.

Що вчитель дивиться на екрані

Крок 10

Шкала виконання

Це те, що бачить учитель, коли виставляє оцінку.

СкладникВагаРезультат
Виконано
0%
Рекомендований бал за 12-бальною

Чотири речі, які треба винести з уроку

  1. Розширення — це підказка, а не факт. Воно живе в імені файлу, а не в самому файлі, і його може написати будь-хто.
  2. Формат визначають перші байти. Сигнатура — це домовленість авторів формату, записана в специфікації.
  3. Сигнатура не завжди на нульовому байті. У MP4 вона з офсету 4, у WAV і WebP типом уточнює вже другий підпис на офсеті 8.
  4. Перейменування ≠ конвертація. Перше міняє напис, друге — структуру всередині. Це тема наступного уроку.

Домашнє завдання

Знайди на власному пристрої три файли різних типів. Для кожного запиши три речі: ім'я, розширення і яку сигнатуру ти очікуєш побачити в hex. Потім перевір себе командою xxd файл | head -1. У PowerShell це Format-Hex. Познач, де твоє припущення справдилося, а де ні. Три рядки в зошиті. На наступному уроці розберемо ті, що не справдилися.

Джерела

Звідки взяті сигнатури

Кожен байт у таблицях цієї сторінки взято зі специфікації формату або з офіційної документації інструмента. Перевіряти дозволено й корисно.

Повний список із поясненнями, що саме звідки взято, — у файлі urok-11-джерела.md поруч із цією сторінкою.