Лекція 11. Файли, заголовки та шістнадцяткові числа
Часто в криміналістиці намагаються знайти докази на жорстких дисках та картах пам’яті, які приносиять поліцейські.
По телевізору та в кінотеатрах цей процес можуть показувати приблизно так, там герої різноманітних телесеріалів та фільмів кажуть фрази “приблизити” та “покращити”, що магічним чином змушує зображення відкривати деталі приховані до цього.
Але коли ми насправді пробуємо “покращити” зображення, ми, врешті решт, бачимо пікселі, з яких складається зображення, тому що в зображенні є тільки обмежена кількість бітів. (Поганий хлопець у відображенні його очей буде мати розмір лишень 6 пікселів, незважаючи на те як сильно ми спробуємо приблизити!)
1. Шістнадцяткові числа
Цікавим є те, що файли зазвичай можуть бути ідентифіковані за визначеним набором бітів. Різні файли, такі як JPEG чи PNG (файл зображення) або GIF (файл зображення) або документ Word чи таблиця Excel, будуть мати різні набори бітів, і ці набори зазвичай розташовані на початку файлу, тож коли комп’ютер відкриває його він може розпізнати, скажімо, зображення JPEG, та вивести його на екран для користувача. Або, файл може бути схожим на документ Word, тож його можна вивести користувачеві як есе.
Наприклад, три перші байти JPEG-файлу:
255 216 255
Фахівці з інформатики схильні записувати числа у шістнадцятковому форматі, а не в десятковому чи двійковому.
Згадаємо, що десяткові числа використовують 10 цифр від 0 до 9, тоді як двійкові складаються з двох цифр - 0 та 1.
Шістнадцятковий запис числа означає що ми маємо 16 цифр: 0-9 та a, b, c, d, e, f.
"a" це 10, "b" це 11, і так далі.
Чим це може бути корисно? Давайте запишемо біти які утворюють ці числа:
255 216 255
11111111 11011000 11111111
Це має сенс, тому що байт складається з 8 бітів і, якщо ми розіб’ємо кожен байт на дві групи по 4 біти, то кожен набір з 4 бітів буде відповідати рівно одному шістнадцятковому розряду:
255 216 255
1111 1111 1101 1000 1111 1111
f f d 8 f f
Щоб зробити цей запис більш зручним, ми приберемо пробіли та додамо на початку 0x, просто щоб позначити що символи у останньому рядку записані у шістнадцятковій формі:
0xff 0xd8 0xff
Зауважте що ми можемо також перетворити дві шістнадцяткові цифри у 8 біт у двійковому записі, або один байт, що є значно зручнішим для представлення двійкових даних.
Інший формат для файлів-зображень це файл бітової мапи, BMP. Один з прикладів зображення у цьому форматі є bliss.bmp, дуже добре відомий набір зелених пагорбів на фоні голубого, хмарного неба (стандартна шпалера Windows XP на мільйонах персональних комп’ютерів).
Бітова мапа це просто послідовність пікселів або точок, “мапа бітів”, якщо забажаєте.
Що цікаво, ці файли не просто починаються з декількох байтів. Їх заголовки складаються з цілої групи чисел, байтів, а їх порядок та значення визначені їх автором, Microsoft. Дійсно, Microsoft назвала типи тих значень як WORD, DWORD і LONG, які є просто типами даних, як, наприклад, int, просто ще декілька назв для тої ж сутності.
Тож, коли хтось клікає по BMP-файлу, зображення відображається тільки тому, що операційна система (або, ймовірніше, програма для перегляду зображень) помітила всі ті біти на початку файлу і розпізнала що це BMP.
3. Файлове введення/виведення
Раніше ми зчитували дані тільки з терміналу та писали дані лише у термінал (читали з stdin, записували в stdout). Але дані можна також зчитувати з файлу або записувати у файл. Запис у файл та зчитування з файлу називаються відповідно файловим введенням та файловим виведенням.
Для зчитування або запису у файл треба:
Створити покажчик на файл.
FILE* file;
Відкрити файл
Файл можна відкрити за допомогою функціїї fopen. Ця функція повертає покажчик на відкритий файл, або NULL, якщо не вдалось відкрити файл. Треба завжди перевіряти, чи був відкритий файл, перед тим як щось із ним робити.
file = fopen("file.txt", "r");
Перший аргумент: шлях до файлу
Другий аргумент - режим
- "r" - читання з файлу
- "w" - запис у файл. Створить файл, якщо він ще не існує.
- "a" - дописування в кінець файлу. Створить файл, якщо він ще не існує.
Зчитати або записати щось у файл:
Функції зчитування з файлу:
- fgetc - повертає наступний символ
- fgets - повертає текстовий рядок
- fread - зчитує певну кількість байтів та записує їх у масив
- fseek - переходить на визначену позицію у файлі
Функції запису у файл:
- fputc - записує символ
- fputs - записує текстовий рядок
- fprintf - записує відформатований рядок у файл
- fwrite - записує масив байтів у файл
Закрити файл:
fclose(file);
Програма, наведена нижче, приймає як аргумент командного рядка назву файлу, який потрібно зчитати, і виводить на екран вміст цього файлу, або помилку, якщо файл не існує.
#include <stdio.h>
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Usage: cat file [file ...]\n");
return 1;
}
for (int i = 1; i < argc; i++) {
FILE* file = fopen(argyfil, "r");
if(file = NULL) { //Перевіряємо чи відкрито файл
printf("cat: %s: No such file or directory\n", argv[i]);
return 1; //Якщо файл не відкрито, отже він не існує - помилка
}
//Цикл, у якому зчитуємо символи з файлу
for(int c = fgetc(file); c != EOF; c = fgetc(file)) {
putchar(c); //Виведення символу на екран
}
fclose(file);
}
return 0;
}