Работа с архивами может пригодиться вашей программе, если необходимо работать с большим количеством мелких файлов. Как правило это относится к играм, в которых различные ресурсы хранятся в виде набора файлов. Естественно, если их много, то они займут большое дисковое простанство в файловой системе любого типа. Поэтому удобно создавать из этих файлов архивы.
В данной ситуации очень помогают библиотеки созданные кем-то ранее и начинаешь понимать силу Open Source. Так вот с помощью исследования просторов интернета и нескольких программ различной направленности я случайно нашел для себя библиотеку libzip (http://nih.at/libzip) для работы с ZIP архивами.
К сожалению в поставке Slackware 12.2 я такой библиотеки не нашел. Поэтому скачиваем ее:
wget http://nih.at/libzip/libzip-0.9.tar.bz2
Распаковываем архив:
$ tar xvjf libzip-0.9.tar.bz2
Переходим внутрь директории с библиотекой и компилируем ее:
cd libzip-0.9 ./configure --prefix=/usr make && make install
Теперь библиотека установлена.
Давайте для теста создадим специальный ZIP архив, а для этого создадим структуру каталогов с файлами:
mkdir /testzip cd /testzip mkdir dir1 mkdir dir2 echo "file 1 in dir 1" > dir1/file1.txt echo "file 2 in dir 1" > dir1/file2.txt echo "file 1 in dir 2" > dir2/file1.txt echo "file 2 in dir 2" > dir2/file2.txt echo "file 1 in root" > file1.txt echo "file 2 in root" > file2.txt
Теперь архивируем это все:
root@darkstar:/testzip# zip test.zip -R * adding: dir1/ (stored 0%) adding: dir1/file1.txt (stored 0%) adding: dir1/file2.txt (stored 0%) adding: dir2/ (stored 0%) adding: dir2/file1.txt (stored 0%) adding: dir2/file2.txt (stored 0%) adding: file1.txt (stored 0%) adding: file2.txt (stored 0%) root@darkstar:/testzip# unzip -l test.zip Archive: test.zip Length Date Time Name -------- ---- ---- ---- 16 03-22-09 09:40 dir1/file1.txt 16 03-22-09 09:40 dir1/file2.txt 16 03-22-09 09:40 dir2/file1.txt 16 03-22-09 09:40 dir2/file2.txt 15 03-22-09 09:40 file1.txt 15 03-22-09 09:40 file2.txt -------- ------- 94 6 files
Теперь давайте напишем простейшую программу для работы с ZIP файлом:
// файл zip1.c #include <stdio.h> #include <stdlib.h> #include <zip.h> int main(int argc, char **argv) { struct zip *zip_file; // дескриптор zip файла int err; // переменая для возврата кодов ошибок int files_total; // количество файлов в архиве if (argc < 2) { fprintf(stderr,"usage: %s <zipfile>\n",argv[0]); return -1; }; // открываем файл zip с именем переданным в качестве параметра zip_file = zip_open(argv[1], 0, &err); if (!zip_file) { fprintf(stderr,"Error: can't open file %s\n",argv[1]); return -1; }; files_total = zip_get_num_files(zip_file); // количество файлов в архиве printf("Files in ZIP: %d\n",files_total); zip_close(zip_file); return 0; };
Компилируем и пробуем запускать:
root@darkstar:/testzip# gcc -lzip zip1.c -o zip1 root@darkstar:/testzip# ./zip1 usage: ./zip1root@darkstar:/testzip# ./zip1 1.zip Error: can't open file 1.zip root@darkstar:/testzip# ./zip1 test.zip Files in ZIP: 6
Видим то, что файл открывается и мы можем узнать сколько в архиве файлов.
Для получения списка имен файлов в архиве давайте создадим файл zip2.c:
// файл zip2.c #include <stdio.h> #include <stdlib.h> #include <zip.h> int main(int argc, char **argv) { struct zip *zip_file; // дескриптор zip файла struct zip_stat file_info; // информация о файле struct tm file_time; int err; // переменая для возврата кодов ошибок int files_total; // количество файлов в архиве int i; if (argc < 2) { fprintf(stderr,"usage: %s <zipfile>\n",argv[0]); return -1; }; // открываем файл zip с именем переданным в качестве параметра zip_file = zip_open(argv[1], 0, &err); if (!zip_file) { fprintf(stderr,"Error: can't open file %s\n",argv[1]); return -1; }; files_total = zip_get_num_files(zip_file); // количество файлов в архиве printf("Files in ZIP: %d\n",files_total); for (i = 0; i < files_total; i++) { // получаем информацию о файле с номером i в структуру file_info zip_stat_index(zip_file, i, 0, &file_info); printf("index=%d ",file_info.index); // номер файла printf("name=\"%s\" ",file_info.name); // имя файла printf("size=%u ",file_info.size); // размер файла localtime_r(&file_info.mtime, &file_time); // дата модификации файла printf("date=\"%02d-%02d-%04d %02d:%02d\"", file_time.tm_mday,file_time.tm_mon+1, file_time.tm_year+1900, file_time.tm_hour, file_time.tm_min); printf("\n"); }; zip_close(zip_file); return 0; };
Компилируем и тестируем:
root@darkstar:/testzip# gcc -lzip zip2.c -o zip2 root@darkstar:/testzip# ./zip1 test.zip Files in ZIP: 6 index=0 name="dir1/file1.txt" size=16 date="22-03-2009 09:40" index=1 name="dir1/file2.txt" size=16 date="22-03-2009 09:40" index=2 name="dir2/file1.txt" size=16 date="22-03-2009 09:40" index=3 name="dir2/file2.txt" size=16 date="22-03-2009 09:40" index=4 name="file1.txt" size=15 date="22-03-2009 09:40" index=5 name="file2.txt" size=15 date="22-03-2009 09:40"
Заметьте, что у нас легко и непринужденно получился аналог команды unzip -l test.zip со своим форматом вывода данных.
Модифицируем немного исходник для того, чтобы извлекать из него данные:
// файл zip3.c #include <stdio.h> #include <stdlib.h> #include <zip.h> int main(int argc, char **argv) { struct zip *zip_file; // дескриптор zip файла struct zip_file *file_in_zip; // дексриптор файла внутри архива int err; // переменая для возврата кодов ошибок int files_total; // количество файлов в архиве int file_number; int r; char buffer[10000]; if (argc < 3) { fprintf(stderr,"usage: %s <zipfile> <fileindex>\n",argv[0]); return -1; }; // открываем файл zip с именем переданным в качестве параметра zip_file = zip_open(argv[1], 0, &err); if (!zip_file) { fprintf(stderr,"Error: can't open file %s\n",argv[1]); return -1; }; file_number = atoi(argv[2]); // номер файла берем из 3 аргумента files_total = zip_get_num_files(zip_file); // количество файлов в архиве if (file_number > files_total) { printf("Error: we have only %d files in ZIP\n",files_total); return -1; }; // открываем файл внутри архива по номеру file_number file_in_zip = zip_fopen_index(zip_file, file_number, 0); if (file_in_zip) { // читаем в цикле содержимое файла и выводим while ( (r = zip_fread(file_in_zip, buffer, sizeof(buffer))) > 0) { printf("%s",buffer); }; // закрываем файл внутри архива zip_fclose(file_in_zip); } else { fprintf(stderr,"Error: can't open file %d in zip\n",file_number); }; zip_close(zip_file); return 0; };
Компилируем и пробуем запустить:
root@darkstar:/testzip# gcc -lzip zip3.c -o zip3 root@darkstar:/testzip# ./zip3 usage: ./zip3root@darkstar:/testzip# ./zip3 test.zip 10 Error: we have only 6 files in ZIP root@darkstar:/testzip# ./zip3 test.zip 5 file 2 in root root@darkstar:/testzip# ./zip3 test.zip 2 file 1 in dir 2
Получили распаковщик файлов. Причем данный функционал невозможно получить от unzip, потому что данная программа не позволяет распаковать указанный файл, а только все файлы сразу с указанием имен файлов, которые требуется пропустить.
Библиотека libzip естественно имеет функции и для добавления файлов в архив, но это можно будет рассмотреть потом или же те, кому это понадобится, самостоятельно найдут решение.
Я не описывал в подробностях структуры и функции библиотеки libzip, так как все достаточно прозрачно и очень похоже на стандартные функции. Те кому окажется интересна эта тема, думаю, без труда смогут самостоятельно найти все ответы на вопросы.