На этом уроке мы рассмотрим работу с файлами и каталогами в Qt5. Для этого мы будем использовать следующие классы:
QFile
, QDir
и QFileInfo
— основные классы для работы с файлами в Qt5;
QFile
— предоставляет интерфейс для чтения и записи информации в файлы;
QDir
— обеспечивает доступ к структуре каталогов и к их содержимому;
QFileInfo
— предоставляет информацию о файле, включая его имя и расположение в файловой системе, время доступа и изменения, имя владельца файла и текущие разрешения.
Поехали!
Размер файла
Для определения размера файла в классе QFileInfo предусмотрен метод size():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <QTextStream> #include <QFileInfo> int main(int argc, char *argv[]) { QTextStream out(stdout); if (argc != 2) { qWarning("Usage: file_size file"); return 1; } // Имя файла передается в качестве аргумента в нашу программу QString filename = argv[1]; // Проверяем существование файла if (!QFile(filename).exists()) { qWarning("The file does not exist"); // если файл не найден, то выводим предупреждение и завершаем работу программы return 1; } // Создаем объект QFileInfo fileinfo(filename); // Определяем размер файла с помощью метода size() qint64 size = fileinfo.size(); // qint64 - это тип данных, который гарантированно будет 64-битным на всех платформах, поддерживаемых Qt // Выводим результат QString str = "The size is: %1 bytes"; out << str.arg(size) << endl; } |
Для запуска программы проделайте следующие шаги:
ПОДГОТОВКА:
Шаг №1: Скомпилируйте вашу программу. Для этого выберите в меню "Сборка" > "Собрать всё"
(или нажмите Ctrl+Shift+B
).
Шаг №2: Зайдите в папку, где лежит ваш Qt-проект (у меня он расположен в C:\dev\Qt_Project
).
Шаг №3: После выполнения первого шага у вас должна появиться еще одна папка, в которой будет создан исполняемый файл программы. Т.к. я использую компилятор MinGW 32-bit и режим компиляции Debug, то при компиляции проекта Qt автоматически создал папку build-My_QtApplication-Desktop_Qt_5_13_0_MinGW_32_bit-Debug
.
Шаг №4: Зайдите в папку debug
(она находится внутри папки, созданной на шаге №3).
Шаг №5: Найдите *.exe-файл вашей программы (у меня это file_size.exe
).
Шаг №6: Скопируйте этот файл в папку к соответствующему компилятору (напомню, т.к. я использовал компилятор MinGW 32-bit, то у меня этот путь выглядит следующим образом: C:\Soft\Qt\5.13.0\mingw73_32\bin
).
ЗАПУСК ПРОГРАММЫ:
Шаг №7: Откройте командную строку.
Шаг №8: Перейдите в папку, указанную в шаге №6 (cd C:\Soft\Qt\5.13.0\mingw73_32\bin
).
Шаг №9: Запустите свою программу, передав ей в качестве параметра имя любого файла (у меня этим файлом оказалась эта же программа, поэтому в моем случае команда была следующей: file_size.exe file_size.exe
).
Результат выполнения программы:
Чтение содержимого файлов
Для того, чтобы прочитать содержимое файла, мы должны сначала открыть этот файл в режиме чтения, затем создать входящий файловый поток, из которого мы будем считывать данные. В примере, приведенном ниже, мы считываем данные из файла C:\colours.txt
. Файл содержит названия семи цветов, вот его содержимое:
Red
Green
Black
Yellow
Purple
Blue
White
А теперь сама программа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <QTextStream> #include <QFile> int main() { QTextStream out(stdout); // Создаем объект QFile file("C:\\colours.txt"); // С помощью метода open() открываем файл в режиме чтения if (!file.open(QIODevice::ReadOnly)) { qWarning("Cannot open file for reading"); // если файл не найден, то выводим предупреждение и завершаем выполнение программы return 1; } // Создаем входящий поток, из которого будут считываться данные, и связываем его с нашим файлом QTextStream in(&file); // Считываем файл строка за строкой while (!in.atEnd()) { // метод atEnd() возвращает true, если в потоке больше нет данных для чтения QString line = in.readLine(); // метод readLine() считывает одну строку из потока out << line << endl; } // Закрываем файл file.close(); } |
Результат выполнения программы:
Запись данных в файл
Для записи данных в файл, мы сначала открываем файл в режиме записи, затем создаем поток вывода, направленный в файл, и используем оператор <<
для записи данных в этот поток. В примере, приведенном ниже, имена пяти дистрибутивов Linux записываются в файл C:\distros.txt
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <QTextStream> #include <QFile> int main() { QTextStream out(stdout); // Создаем объект класса QFile и связываем его с указанным именем файла QString filename = "C:\\distros.txt"; QFile file(filename); // Открываем файл в режиме "Только для записи" if (file.open(QIODevice::WriteOnly)) { QTextStream out(&file); // поток записываемых данных направляем в файл // Для записи данных в файл используем оператор << out << "Xubuntu" << endl; out << "Arch" << endl; out << "Debian" << endl; out << "Redhat" << endl; out << "Slackware" << endl; } else { qWarning("Could not open file"); } // Закрываем файл file.close(); } |
В результате по заданному вами пути программа создаст файл со следующим содержимым:
Копирование файла
Когда мы копируем файл, мы создаем точную копию файла с другим именем или же в другом месте файловой системы. В следующем примере создается копия указанного файла с помощью метода QFile::copy()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <QTextStream> #include <QFile> #include <conio.h> int main(int argc, char *argv[]) { QTextStream out(stdout); out.setCodec("cp-866"); // Программа принимает 2 параметра if (argc != 3) { qWarning("Usage: copyfile source destination"); // если параметров нет, то выводится предупреждение return 1; } // Из аргументов командной строки программы мы получаем имя исходного файла QString source = argv[1]; // Проверяем наличие исходного файла if (!QFile(source).exists()) { qWarning("The source file does not exist"); // если файл не найден, то выводим предупреждение и завершаем выполнение программы return 1; } // Получаем имя файла назначения QString destin(argv[2]); // Копируем исходный файл QFile::copy(source, destin); // первый параметр - это имя исходного файла, а второй - имя конечного файла } |
Примечание: Для запуска программы см. пункт «Размер файла» (подпункты «Подготовка» и «Запуск программы»).
Результат выполнения программы:
Владелец файла и группы
Примечание: Выполнение данной функции может занимать довольно много времени в Unix и в Windows (в порядке миллисекунд). В Windows функция возвращает пустую строку, если не включена проверка разрешений NTFS. В файловых системах NTFS проверка прав собственности и разрешений по умолчанию отключена как раз из соображений производительности. Чтобы включить проверку, добавьте в вашу программу следующие строки:
1 2 |
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; qt_ntfs_permission_lookup++; // включение проверки |
Каждый файл имеет конкретного пользователя, который является его владельцем. Файл также принадлежит некоторой группе пользователей. В следующем примере выводится информация о владельце и основной группе файла, с которым мы работаем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <QTextStream> #include <QFileInfo> int main(int argc, char *argv[]) { extern Q_CORE_EXPORT int qt_ntfs_permission_lookup; qt_ntfs_permission_lookup++; // включение проверки QTextStream out(stdout); if (argc != 2) { qWarning("Usage: owner file"); return 1; } QString filename = argv[1]; // Создаем объект класса QFileInfo. В качестве параметра используем имя файла из аргумента командной строки QFileInfo fileinfo(filename); // Определяем основную группу файла QString group = fileinfo.group(); // Определяем владельца файла QString owner = fileinfo.owner(); out << "Group: " << group << endl; out << "Owner: " << owner << endl; } |
Примечание: Для запуска программы см. пункт «Размер файла» (подпункты «Подготовка» и «Запуск программы»).
Результат выполнения программы:
Время последнего чтения/изменения файла
Файлы хранят информацию о последнем времени их чтения/изменения. Чтобы получить эту информацию, мы будем использовать класс QFileInfo. В следующем примере мы выводим время последнего чтения и последнего изменения указанного файла:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <QTextStream> #include <QFileInfo> #include <QDateTime> #include <conio.h> int main(int argc, char *argv[]) { QTextStream out(stdout); out.setCodec("cp-866"); if (argc != 2) { qWarning("Usage: file_times file"); return 1; } QString filename = argv[1]; // Создаем объект класса QFileInfo. В качестве параметра используем имя файла из аргумента командной строки QFileInfo fileinfo(filename); // Определяем дату и время последнего чтения файла QDateTime last_rea = fileinfo.lastRead(); // Определяем дату и время последнего изменения файла QDateTime last_mod = fileinfo.lastModified(); out << "Last read: " << last_rea.toString() << endl; out << "Last modified: " << last_mod.toString() << endl; } |
Примечание: Для запуска программы см. пункт «Размер файла» (подпункты «Подготовка» и «Запуск программы»).
Результат выполнения программы:
Работа с каталогами
Для работы с каталогами (папками) в Qt имеется соответствующий класс QDir. В следующем примере мы рассмотрим 4 метода для работы с каталогами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <QTextStream> #include <QDir> #include <conio.h> int main() { QTextStream out(stdout); out.setCodec("cp-866"); QDir dir; // Метод mkdir() создает каталог, при этом возвращая true, если каталог был успешно создан if (dir.mkdir("mydir")) { out << "mydir successfully created" << endl; } dir.mkdir("mydir2"); // Метод exists() проверяет наличие каталога if (dir.exists("mydir2")) { dir.rename("mydir2", "newdir"); // метод rename() переименовывает каталог } // Создаем новый каталог и все необходимые родительские каталоги dir.mkpath("temp/newdir"); } |
Примечание: Для запуска программы см. пункт «Размер файла» (подпункты «Подготовка» и «Запуск программы»).
Результат выполнения программы:
Специальные пути
В файловой системе существуют некоторые специальные пути (например, домашняя директория или корневой каталог). Для получения специальных путей, заданных в системе, используется класс QDir. Рассмотрим пример, в котором выводятся 4 специальных пути:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <QTextStream> #include <QDir> #include <conio.h> int main() { QTextStream out(stdout); out.setCodec("cp-866"); out << "Current path:" << QDir::currentPath() << endl; // определяем и выводим путь текущего рабочего каталога out << "Home path:" << QDir::homePath() << endl; // определяем и выводим путь домашнего каталога out << "Temporary path:" << QDir::tempPath() << endl; // определяем и выводим путь временного каталога out << "Rooth path:" << QDir::rootPath() << endl; // определяем и выводим путь корневого каталога } |
Результат выполнения программы:
Путь к файлу
Файл идентифицируется по имени и по пути. Путь состоит из имени файла, базового имени и суффикса. В следующем примере мы используем несколько методов для вывода пути к файлу и его частей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <QTextStream> #include <QFileInfo> #include <conio.h> int main(int argc, char *argv[]) { QTextStream out(stdout); out.setCodec("cp-866"); if (argc != 2) { out << "Usage: file_times file" << endl; return 1; } QString filename = argv[1]; // Определяем путь к файлу QFileInfo fileinfo(filename); QString absPath = fileinfo.absoluteFilePath(); // возвращаем абсолютный путь, включающий имя файла QString baseName = fileinfo.baseName(); // возвращаем базовое имя: имя файла без пути QString compBaseName = fileinfo.completeBaseName(); // возвращаем полное базовое имя: все символы в имени файла до последней точки (но не включая её) QString fileName = fileinfo.fileName(); // возвращаем имя файла, которое является базовым именем + расширение QString suffix = fileinfo.suffix(); // возвращаем расширение файла, которое состоит из всех символов в базовом имени файла + расширение после последнего символа точки (но не включая её) QString compSuffix = fileinfo.completeSuffix(); // возвращаем расширение файла, которое состоит из всех символов в базовом имени файла + расширение после первого символа точки (но не включая её) out << "Absolute file path: " << absPath << endl; out << "Base name: " << baseName << endl; out << "Complete base name: " << compBaseName << endl; out << "File name: " << fileName << endl; out << "Suffix: " << suffix << endl; out << "Whole suffix: " << compSuffix << endl; } |
Примечание: Для запуска программы см. пункт «Размер файла» (подпункты «Подготовка» и «Запуск программы»).
Результат выполнения программы:
Права доступа
Файлы в файловой системе имеют систему защиты: флаги, которые определяют, кто может получить доступ к файлам и изменять их, а кто — нет. Метод QFile::permissions()
возвращает перечисление флагов для рассматриваемого файла. В нижеследующем примере создается Unix-подобный список разрешений для указанного файла.
Существует 3 типа возможных пользователей:
владелец;
группа, к которой принадлежит файл;
все остальные пользователи, именуемые others
.
Первые три позиции показывают права владельца файла, следующие три позиции — группы, в которую входит файл, а последние три символа показывают права остальных пользователей.
Существует 4 вида прав:
чтение (r
);
запись или изменение (w
);
выполнение (x
);
отсутствие прав (-
).
Примечание: Следующий пример работает только в Unix-системах.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#include <QTextStream> #include <QFile> int main(int argc, char *argv[]) { QTextStream out(stdout); if (argc != 2) { out << "Usage: permissions file" << endl; return 1; } QString filename = argv[1]; // Получаем перечисление флагов разрешений QFile::Permissions ps = QFile::permissions(filename); // Эта строка динамически строится на основе заданных разрешений QString fper; // Во всех условиях, приведенных ниже, используем оператор &, чтобы определить, из каких возможных флагов прав состоит возвращаемое перечисление if (ps & QFile::ReadOwner) { fper.append('r'); } else { fper.append('-'); } if (ps & QFile::WriteOwner) { fper.append('w'); } else { fper.append('-'); } if (ps & QFile::ExeOwner) { fper.append('x'); } else { fper.append('-'); } if (ps & QFile::ReadGroup) { fper.append('r'); } else { fper.append('-'); } if (ps & QFile::WriteGroup) { fper.append('w'); } else { fper.append('-'); } if (ps & QFile::ExeGroup) { fper.append('x'); } else { fper.append('-'); } if (ps & QFile::ReadOther) { fper.append('r'); } else { fper.append('-'); } if (ps & QFile::WriteOther) { fper.append('w'); } else { fper.append('-'); } if (ps & QFile::ExeOther) { fper.append('x'); } else { fper.append('-'); } out << fper << endl; } |
Результат выполнения программы:
Владелец файла имеет право прочитать файл и изменить его. Группа пользователей, которой принадлежит файл, а также другие пользователи, имеют право читать файл. Поскольку файл не является исполняемым, то нет никаких прав на выполнение файла.
Содержимое каталога
В следующем примере мы рассмотрим работу с содержимым указанного каталога. Для перечисления содержимого каталога используется класс QDir и его метод entryInfoList(). Список файлов отсортирован по размеру, при этом вывод идет в два столбца: первый столбец содержит имена файлов, а второй — размеры файлов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#include <QTextStream> #include <QFileInfo> #include <QDir> #include <conio.h> int main(int argc, char *argv[]) { QTextStream out(stdout); out.setCodec("cp-866"); if (argc != 2) { qWarning("Usage: list_dir directory"); return 1; } QString directory = argv[1]; // Создаем объект класса QDir с заданным именем каталога QDir dir(directory); if (!dir.exists()) { qWarning("The directory does not exist"); return 1; } // Метод setFilter() определяет тип файлов, которые должны быть возвращены методом entryInfoList() dir.setFilter(QDir::Files | QDir::AllDirs); // Метод setSorting() задает порядок сортировки, используемый методом entryInfoList() dir.setSorting(QDir::Size | QDir::Reversed); // Метод entryInfoList() возвращает список объектов QFileInfo для всех файлов и папок в каталоге, отфильтрованных и упорядоченных соответствующими методами QFileInfoList list = dir.entryInfoList(); int max_size = 0; // Проходимся по списку и определяем максимальный размер имени файла foreach (QFileInfo finfo, list) { QString name = finfo.fileName(); int size = name.size(); if (size > max_size) { max_size = size; } } // Добавляем дополнительные два пробела к длине столбца int len = max_size + 2; // Выводим имена столбцов out << QString("Filename").leftJustified(len).append("Bytes") << endl; // метод leftJustified() возвращает строку заданного размера, выравненную по левому краю и дополненную символом заполнения (по умолчанию пробел) справа // Просматриваем список файлов и выводим их названия и размеры. Первый столбец выравнивается по левому краю и заполняется пробелами по мере необходимости, затем добавляется второй столбец for (int i = 0; i < list.size(); ++i) { QFileInfo fileInfo = list.at(i); QString str = fileInfo.fileName().leftJustified(len); str.append(QString("%1").arg(fileInfo.size())); out << str << endl; } return 0; } |
Примечание: Для запуска программы см. пункт «Размер файла» (подпункты «Подготовка» и «Запуск программы»).
Результат выполнения программы:
Заключение
Это последняя статья, в которой возможности Qt5 рассматриваются на примере консольного приложения. На следующих уроках данного туториала мы начнем работать с GUI + Widgets.
Как сделать чтобы полученный файл можно было запустить не только из папки компилятора?
Дмитрий Бушуев, спасибо Вам за уроки! Надеюсь Вы расширите их число)
Всегда пожалуйста 🙂
Копирование файла.
Можно поподрбнее про такой момент:
сначала у нас main(… *argv[]), т.е. один из параметров- указатель на первый элемент массива чар.
Привызове функции, создаются локальные копии аргументов.
Дальше в тексте: QString source = argv[1];
Вот как это понять? в переменную source передается первый элемент массива?
Да, argv[1] это как раз строка пути к нашему файлу, которую мы передаем в нашу функцию main при вызове программы из командной строки.
Спасибо, уже разобрался.
Наткнулся на ролик, где чел объясняет что это за argc, argv[], очень просто и понятно, сам-бы никогда не допетрил.
Рад, что у вас получилось разобраться 🙂
Круто! Блин, жаль, что так мало людей отписались в коментах, видать и прочитали эти уроки немногие((
Можно нубских вопросов?
1) Пример про определение размера файла.
Почему int main(int argc, char *argv[]), а в следующем примере просто int main() ?
Здравствуйте, Sanchosd. Я конечно сам новичок на с++, но скажу, как я понимаю. "int argc, char *argv[]" — это 2 необязательных параметра основной функции "main". int argc — количество входящих параметров в функцию main, char *argv[] — массив строк самих параметров. Как вы видите в командной строке мы вводим сначала название нашего файла, а через пробел файл, который будет использован в нашей функции. Т.е. если они указаны (main(int argc, char *argv[])), то мы можем передать при открытии некие параметры и использовать их, если же в main этих параметров нет (main()), то никакие параметры не нужны.
Спасибо Михалыч за помощь!
Уже разобрался сам, но всегда приятно, когда кто-то откликнулся и помог!
Подскажите, как реализовать запись/чтение смешанных данных.
мне нужно получить файл где будет текст и двоичные данные
думаю будет проще, если нужно и двоичный, и текст, открыть файл как двоичный файл. При открытии файла в двоичном виде, он будет почти так же работать с текстом, как если открыть как текст (известный мне случай различия относится к переносу строки "\n" or "\r\n")
Привет, спасибо за статью. Еще ожидал увидеть как объекты собственных классов можно записать в файл. Какие операторы преобразования типов нужно написать.
пример тут: https://stackoverflow.com/questions/14375795/best-way-to-write-a-custom-class-to-a-file-using-qt
"Для запуска программы проделайте следующие шаги" — сделано очень много и, имхо, совсем не дружелюбно к пользователю.
Вы, полагаю, работали в Qt Creator, там можно намного проще: на вкладке Проекты на левой панели найти раздел Запуск, и строку с вводом "Параметры командной строки". Записать в нее путь до требуемого файла. Собственно, все, остаётся только запустить программу соответствующей кнопкой
Замечание по копированию файла, на что я напоролся(пытался перезаписать существующий файл): "Note that if a file with the name newName already exists, copy() returns false (i.e. QFile will not overwrite it)." , т.е. файла-приемника НЕ должно быть, мне кажется, это стоило бы отразить в статье
>Замечание по копированию файла […], мне кажется, это стоило бы отразить в статье.
—
Да, QFile::copy() не перезаписывает файл, если уже существует файл с таким же именем. В статье это косвенно отражено фразой: "мы создаём точную копию файла с другим именем или же в другом месте файловой системы."
Думаю, что действительно в статье этот момент стоит явно обозначить. Спасибо за замечание 🙂
Чтобы реализовать возможность перезаписи уже существующего файла, то нужно дополнить код проверкой на существование конечного файла. Если такой файл существует, то нужно удалить, а потом уже копировать новый файл:
>"Для запуска программы проделайте следующие шаги" — сделано очень много и, имхо, совсем не дружелюбно к пользователю.
—-
Если 9 простейших, подробно расписанных шагов (с картинками) работы с проводником и командной строкой, вызывают трудности у пользователя, то может ему стоит сначала прочитать какую-нибудь книгу по работе с ПК, а уж потом браться за изучение C++/Qt?
Есть служба, которая следит за каталогом с довольно большим, от сотен до тысяч, количеством файлов. На основе анализа содержимого файлов строятся отчеты (до пяти отчетов разных видов). Если содержимое какого-либо файла меняется, либо меняется количество файлов, построение отчетов прерывается и начинается заново. Функциональность построения отчета реализована в классе Начал с чтения документации и примеров Qt. Во всех примерах поток создается наследованием класса QThread и переопределением метода