Работа с папками

Теги: libuv.h, файловая система, кроссплатформенная работа с папками, директории libuv, создать папку, прочитать файлы в папке, удаление папки, временная директория



Чтение файлов в директории

В стандарте си отсутствует реализация работы с папками – ни создания, ни чтения директории, ничего. Для работы с папками в кроссплатформенной библиотеке libuv есть набор методов. Но сначала рассмотрим особенности файловой системы. Библиотека libuv выделяет следующие типы файлов, описанные с помощью перечисляемого типа uv_dirent_type_t:

  • UV_DIRENT_UNKNOWN – тип не известен
  • UV_DIRENT_FILE – обыкновенный файл
  • UV_DIRENT_DIR – папка
  • UV_DIRENT_LINK – ссылка (ярлык)
  • UV_DIRENT_FIFO – специализированный файл для межпроцессорного взаимодействия
  • UV_DIRENT_SOCKET – сокет
  • UV_DIRENT_CHAR - Специальный файл для передачи данных посимвольно, например, от клавиатуры, мыши, последовательного порта. Примеры файлов в ОС Linux это /dev/autofs, /dev/console, /dev/crash, /dev/lp0, /dev/null, /dev/ppp, /dev/random, /dev/tty
  • UV_DIRENT_BLOCK - Специальный файл для передачи данных блочно, например, от клавиатуры, мыши, последовательного порта. Примеры файлов в ОС Linux это /dev/loop0, /dev/ram0, /dev/sda1, /dev/sr0

Для чтения содержимого папки сначала производим запрос uv_fs_scandir

int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb)

Все аргументы функции нам уже знакомы. Результатом выполнения (напомню, результат – это поле result запроса, который будет передан колбэку) будет число считанных объектов в папке. Для прохода по всем элементам используется функция uv_fs_scandir_next

int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent)

где req – это полученный колбэком запрос, а второй аргумент – это структура типа uv_dirent_t. Эта структура после каждой итерации будет иметь интересующие нас значения – поле name с именем объекта, и поле type типа uv_dirent_type_t, которое определяет тип объекта (о нём говорилось в самом начале).

Для тестирования создадим папку с тремя файлами, папкой и ссылкой.

В тестовой папке три файла, одна ссылка и одна папка
В тестовой папке три файла, одна ссылка и одна папка

Код программы для чтения списка файлов в каталоге

#include <uv.h>
#include <stdio.h>
#include <fcntl.h>
#ifdef _WIN32
#include <conio.h>
#include <Windows.h>
#define Sleep(x) Sleep(x)
#define wait() _getch()
#else
#include <unistd.h>
#define Sleep(x) sleep(x)
#define wait() scanf("1");
#endif

uv_loop_t* loop;

uv_fs_t scan_req;
uv_fs_t scan_next_req;
uv_fs_t close_req;

void scan_cb(uv_fs_t*);
void close_cb(uv_fs_t*);
const char* name_by_type(uv_dirent_type_t);

const char *dirpath = "C:/c/test_scan";

int main(int argc, char **argv) {
	int r;

	loop = uv_loop_new();

	r = uv_fs_scandir(loop, &scan_req, dirpath, O_RDONLY, scan_cb);
	if (r < 0) {
		printf("Error at opening file: %s\n", uv_strerror(r));
	}

	uv_run(loop, UV_RUN_DEFAULT);
	uv_loop_close(loop);
	return 0;
}

void scan_cb(uv_fs_t *req) {
	int r, i;
	uv_dirent_t dent;

	while (UV__EOF != uv_fs_scandir_next(req, &dent)) {
		printf("%s\t[%s]\n", dent.name, name_by_type(dent.type));
	}
	r = uv_fs_close(loop, &close_req, req->file.fd, close_cb);
	uv_fs_req_cleanup(req);
}

void close_cb(uv_fs_t* req) {
	wait();
}

const char* name_by_type(uv_dirent_type_t type) {
	switch (type) {
	case UV_DIRENT_UNKNOWN:
		return "Unknown";
	case UV_DIRENT_FILE:
		return "File";
	case UV_DIRENT_DIR:
		return "Directory";
	case UV_DIRENT_LINK:
		return "Link";
	case UV_DIRENT_FIFO:
		return "FIFO special file";
	case UV_DIRENT_SOCKET:
		return "Socket";
	case UV_DIRENT_CHAR:
		return "Char file";
	case UV_DIRENT_BLOCK:
		return "Block file";
	}
}

Создание новой директории

Для создания новой директории есть две функции

int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb)

создаёт новый каталог. Стоит заметить, что аргумент mode не используется в ОС Windows. Также можно использовать функцию

int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb)

которая создаёт каталог с уникальным именем. При этом аргумент tpl – это шаблон имени, в котором последние 6 символов буду заменены на случайный набор символов.

Пример создания папки. Далее все директивы пропущены, чтобы уменьшить размер программы

uv_loop_t* loop;
 
uv_fs_t mkdir_req;
uv_fs_t close_req;
 
void mkdir_cb(uv_fs_t*);
void close_cb(uv_fs_t*);
 
const char *dirpath = "C:/c/test_dir";
 
int main(int argc, char **argv) {
    int r;
 
    loop = uv_loop_new();
 
    r = uv_fs_mkdir(loop, &mkdir_req, dirpath, O_RDONLY, mkdir_cb);
    if (r < 0) {
        printf("Error at making directory: %s\n", uv_strerror(r));
    }
 
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    return 0;
}
 
void mkdir_cb(uv_fs_t* req) {
    int r = req->result;
    uv_fs_close(loop, &close_req, req->file.fd, close_cb);
    uv_fs_req_cleanup(req);
}
 
void close_cb(uv_fs_t* req) {
    printf("wait");
    wait();
}

При создании папки с уникальным именем её имя будет в поле path запроса

uv_loop_t* loop;
 
uv_fs_t mkdir_req;
uv_fs_t close_req;
 
void mkdir_cb(uv_fs_t*);
void close_cb(uv_fs_t*);
 
const char *template = "C:/c/my-folder-template-XXXXXX";
 
int main(int argc, char **argv) {
    int r;
 
    loop = uv_loop_new();
 
    r = uv_fs_mkdtemp(loop, &mkdir_req, template, mkdir_cb);
    if (r < 0) {
        printf("Error at making directory: %s\n", uv_strerror(r));
    }
 
    uv_run(loop, UV_RUN_DEFAULT);
    uv_loop_close(loop);
    return 0;
}
 
void mkdir_cb(uv_fs_t* req) {
    int r = req->result;
    printf("tmp folder name = %s", req->path);
    uv_fs_close(loop, &close_req, req->file.fd, close_cb);
    uv_fs_req_cleanup(req);
}
 
void close_cb(uv_fs_t* req) {
    wait();
}

Удаление директории

Для удаления папки в кроссплатформенной библиотеке libuv используется функция

int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb)

Важно, что директория должна быть пустой.

Пример программы для удаления пустой директории

#include <uv.h>
#include <stdio.h>
#include <fcntl.h>
#ifdef _WIN32
#include <conio.h>
#define wait() _getch()
#else
#define wait() scanf("1");
#endif

uv_loop_t* loop;
uv_fs_t remove_dir_req;

void remove_dir_cb(uv_fs_t*);

const char *dirname_remove = "C:/c/toremove";

int main(int argc, char **argv) {
	int r;

	loop = uv_default_loop();
	r = uv_fs_rmdir(loop, &remove_dir_req, dirname_remove, remove_dir_cb);
	if (r) {
		printf("Error when remove directory: %s\n", uv_strerror(r));
	}
	uv_run(loop, UV_RUN_DEFAULT);
	return 0;
}

void remove_dir_cb(uv_fs_t* req) {
	int r = req->result;
	printf("remove callback with status %d", r);
	uv_fs_req_cleanup(&remove_dir_req);
	wait();
}
Q&A

Всё ещё не понятно? – пиши вопросы на ящик email
Работа с файловой системой: открытие, чтение, запись и удаление