В ожидании задачника четвертого уровня я несколько дней просматривал фотографии, сохраненные моей цифровой камерой на карту памяти объемом 1 GB CompactFlash (CF). Фотки записаны в формате JPEG. Только не говорите мне, пожалуйста, что на самом деле я провел последние несколько дней на Facebook вместо этого. К сожалению, мои навыки владения компьютером оставляют желать лучшего, и я, сам того не ведая, случайно удалил мои драгоценные фоточки! К счастью, в цифровом мире «удален», как правило, не равняется «убит». Мой компьютер настаивает на том, что карта-памяти теперь пуста, но я-то знаю, что он лжёт.

Посему, вот ваше третье задание на эту неделю: Напишите в ~/workspace/pset4/jpg/recover.c программу, которая восстановит мои фотографии.

Хммм… Сложно, да? Ладно, вот ещё кое-какое уточнение. Несмотря на то, что JPEG-формат сложнее, чем BMP, в него встроена полезная подсказка: JPEG имеет «подписи», шаблоны байтов, которые помогут отличить их от других форматов файлов. Большинство JPEG-файлов начинается со следующих трех байтов:

0xff 0xd8 0xff

От первого байта до третьего, слева направо. Четвертый байт, скорее всего, будет одной из следующих комбинаций: 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,0xe8, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef. Иными словами, первые четыре бита четвертого байта JPEG-файла —1110.

Скорее всего, если вы найдете один из этих шаблонов на диске, на котором хранились фотографии (например, моя карта памяти), это и будет начало JPEG-файла. Конечно, вы можете столкнуться с такой комбинацией на диске случайно… Восстановление данных нельзя назвать точной наукой.

Как решать

  • Открыть файл с содержимым карты памяти
  • Найти начало JPEG-файла. Все файлы на этой карте являются картинками в JPEG-формате.

О маркерах JPEG вы уже знаете:

Задание №3: recover - 1

Важно (и приятно) осознавать, что каждый JPEG-файл сохраняется в памяти единым блоком, а сами файлы следуют один за другим. Изобразим схематическую карту памяти. Каждый прямоугольник — блок длиной 512 байт. Серые прямоугольники — области, где файлы JPEG отсутствуют, звездочками обозначено начало JPEG-файла. Полагаем, что серых блоков между файлами у нас нет.

Задание №3: recover - 2

Как нам прочитать эти данные, эти 512 байтов, чтобы сравнить их начало с заголовком JPEG? Можно воспользоваться уже знакомой нам функцией fread, которая принимает указатель на структуру данных, куда будут записаны прочитанные байты, а также размер элемента, который прочитывается, количество таких элементов и указатель на файл, из которого мы читаем данные.

Задание №3: recover - 3

Мы хотим прочитать 512 байт и сохранить их в буфере. Им будет служить указатель &data, а указатель inptr будет указывать на открытый файл с содержимым карты памяти. Итак, вернемся к структуре нашего файла, в котором сохранено содержимое карты памяти. Мы собираемся читать блоки по 512 байт и сохранять их в буфер, пока не будем знать, что с этим буфером делать. Сначала буфер пуст. Мы читаем первый блок данных, сравниваем его с маркером JPEG и идем дальше.

  • Как только вы находите начало JPEG, вы создаете новый файл на диске. Это можно сделать с помощью функции sprint, в которую передается шаблон и аргумент.
  • …и записываете в него порциями по 512 байт, пока не найдете начало следующего JPEG с помощью функции fwrite.
  • Задание №3: recover - 4
  • «Порции» можно записывать, пока исходный файл карты памяти не закончится. fread возвращает количество элементов размера size, которые были успешно прочитаны. В идеале size должно совпасть c number. Нам нужно организовать условный оператор, который позволит посчитать, сколько элементов на самом деле было прочитано.
  • Задание №3: recover - 5