在Qt5 C++编程教程的这一部分,我们要处理文件和目录。
QFile、QDir和QFileInfo是Qt5中处理文件的基本类。QFile 提供了一个读取和写入文件的接口。QDir 提供了对目录结构和其内容的访问。QFileInfo 提供与系统无关的文件信息,包括文件的名称和在文件系统中的位置、访问时间和修改时间、权限或文件所有权。
Qt5 文件大小
下面是一个求文件大小的例子。
file_size.cpp
#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];
QFile f{filename};
if (!f.exists()) {
qWarning("The file does not exist");
return 1;
}
QFileInfo fileinfo{filename};
qint64 size = fileinfo.size();
QString str = "The size is: %1 bytes";
out << str.arg(size) << endl;
return 0;
}
QString filename = argv[1];
文件名作为程序的参数传递。
QFile f{filename};
if (!f.exists()) {
qWarning("The file does not exist");
return 1;
}
使用 QFile 类的 exists 方法检查文件是否存在。如果不存在,我们发出警告并终止程序。
QFileInfo fileinfo{filename};
创建 QFileInfo 的实例。
qint64 size = fileinfo.size();
使用 size 方法确定文件的大小。qint64 是一种在 Qt 支持的所有平台上都保证为 64 位的类型。
QString str = "The size is: %1 bytes";
out << str.arg(size) << endl;
结果输出到控制台。
$ ./filesize Makefile
The size is: 19993 bytes
Qt5读取文件
为了读取文件的内容,我们必须首先以只读模式打开文件。然后创建输入文件流;从该流中读取数据。
words.txt
sky
blue
cloud
falcon
forest
lake
cup
bear
wolf
我们有一个样本文本文件。
read_file.cpp
#include <QTextStream>
#include <QFile>
int main(void) {
QTextStream out{stdout};
QFile f{"words.txt"};
if (!f.open(QIODevice::ReadOnly)) {
qWarning("Cannot open file for reading");
return 1;
}
QTextStream in{&f};
while (!in.atEnd()) {
QString line = in.readLine();
out << line << endl;
}
return 0;
}
该例子从word.txt文件中读取数据。
QFile f{"words.txt"};
创建一个QFile对象的实例。 QFile在范围外时自动关闭文件。
if (!f.open(QIODevice::ReadOnly)) {
qWarning("Cannot open file for reading");
return 1;
}
QFile的open方法以只读模式打开文件。如果该方法失败,我们发出警告并终止程序。
QTextStream in{&f};
创建输入流。 QTextStream接收文件句柄。数据将从此流中读取。
while (!in.atEnd()) {
QString line = in.readLine();
out << line << endl;
}
在while循环中,我们逐行读取文件,直到文件结束。 atEnd方法如果没有更多数据从流读取,则返回true。readLine方法从流读取一行。
$ ./readfile
sky
blue
cloud
falcon
forest
lake
cup
bear
wolf
Qt5写文件
为了写入文件,我们在写入模式下打开文件,创建一个向文件输出的输出流,并使用写操作符写入该流。
write_file.cpp
#include <QTextStream>
#include <QFile>
int main(void) {
QTextStream out{stdout};
QString filename = "distros.txt";
QFile f{filename};
if (f.open(QIODevice::WriteOnly)) {
QTextStream out{&f};
out << "Xubuntu" << endl;
out << "Arch" << endl;
out << "Debian" << endl;
out << "Redhat" << endl;
out << "Slackware" << endl;
} else {
qWarning("Could not open file");
}
return 0;
}
这个例子将五个Linux发行版的名称写入名为distrost.txt的文件中。
QString filename = "distros.txt";
QFile f{filename};
使用提供的文件名创建QFile对象。
if (f.open(QIODevice::WriteOnly)) {
使用open方法,我们以只写方式打开文件。
QTextStream out{&f};
这一行创建了一个操作文件句柄的QTextStream。换句话说,要写入的数据流被导向到文件。
out << "Xubuntu" << endl;
out << "Arch" << endl;
out << "Debian" << endl;
out << "Redhat" << endl;
out << "Slackware" << endl;
使用<<运算符写入数据。
$ ./writefile
$ cat distros.txt
Xubuntu
Arch
Debian
Redhat
Slackware
复制文件
当我们复制文件时,我们创建一个文件的精确复制,具有不同的名称或在文件系统的不同位置。
copy_file.cpp
#include <QTextStream>
#include <QFile>
int main(int argc, char *argv[]) {
QTextStream out{stdout};
if (argc != 3) {
qWarning("Usage: copyfile source destination");
return 1;
}
QString src = argv[1];
if (!QFile{src}.exists()) {
qWarning("The source file does not exist");
return 1;
}
QString dest(argv[2]);
QFile::copy(src, dest);
return 0;
}
这个例子使用QFile :: copy方法创建提供文件的副本。
if (argc != 3) {
kotlinCopy codeqWarning("Usage: copyfile source destination");
return1;
}
该程序需要两个参数;如果没有给出,则以警告消息结束。
QString src = argv[1];
从程序的命令行参数中获取源文件的名称。
if (!QFile{src}.exists()) {
qWarning("The source file does not exist");
return 1;
}
我们使用QFile的exists方法检查源文件的存在性。如果不存在,则以警告消息结束程序。
QString dest(argv[2]);
我们获取目标文件名。
QFile::copy(src, dest);
使用QFile :: copy方法复制源文件。
第一个参数是源文件名,第二个参数是目标文件名。
每个文件都有一个拥有者。为了更好地管理和保护文件,文件还属于一组用户。
主程序:
owner_group.cpp
#include <QTextStream>
#include <QFileInfo>
int main(int argc, char *argv[]) {
QTextStream out{stdout};
if (argc != 2) {
qWarning("Usage: owner file");
return 1;
}
QString filename = argv[1];
QFileInfo fileinfo{filename};
QString group = fileinfo.group();
QString owner = fileinfo.owner();
out << "Group: " << group << endl;
out << "Owner: " << owner << endl;
return 0;
}
这个例子打印给定文件的拥有者和主组。
QFileInfo fileinfo{filename};
创建了QFileInfo类的实例。其参数是作为命令行参数给出的文件名。
QString group = fileinfo.group();
使用QFileInfo的group方法确定文件的主组。
QStringowner= fileinfo.owner();
使用QFileInfo的owner方法确定文件的拥有者。
$ touch myfile
$ ./ownergroup myfile
Group: janbodnar
Owner: janbodnar
Qt5文件最后阅读和最后修改时间
文件存储有关最后一次阅读或修改的信息。为了获取这些信息,我们使用QFileInfo类。
file_times.cpp
#include <QTextStream>
#include <QFileInfo>
#include <QDateTime>
int main(int argc, char *argv[]) {
QTextStream out{stdout};
if (argc != 2) {
qWarning("Usage: file_times file");
return 1;
}
QString filename = argv[1];
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;
return 0;
}
这个示例打印出的最后阅读和最后修改的时间是2022年1月6日
QFileInfo fileinfo{filename};
创建QFileInfo对象。
QDateTime last_rea = fileinfo.lastRead();
lastRead方法返回文件最后一次被读取(访问)的日期和时间。
QDateTime last_mod = fileinfo.lastModified();
lastModified方法返回文件最后一次被修改的日期和时间。
$ ./filetimes Makefile
Last read: Sun Dec 6 12:46:11 2020
Last modified: Sun Dec 6 12:46:10 2020
Qt5与目录一起工作
QDir类具有处理目录的方法。
dirs.cpp
#include <QTextStream>
#include <QDir>
int main(void) {
QTextStream out{stdout};
QDir dir;
if (dir.mkdir("mydir")) {
out << "mydir successfully created" << endl;
}
dir.mkdir("mydir2");
if (dir.exists("mydir2")) {
dir.rename("mydir2", "newdir");
}
dir.mkpath("temp/newdir");
return 0;
}
在这个例子中,我们介绍了四种处理目录的方法。
if (dir.mkdir("mydir")) {
out << "mydir successfully created" << endl;
}
mkdir方法创建目录。如果目录成功创建,它将返回true。
if (dir.exists("mydir2")) {
dir.rename("mydir2", "newdir");
}
exists检查目录是否存在。
rename方法重命名目录。
dir.mkpath("temp/newdir");
mkpath一次创建新目录和所有必要的父目录。
Qt5特殊路径
在文件系统中有一些特殊的路径,例如主目录或根目录。使用 QDir 类获取系统中的特殊路径。
special_paths.cpp
#include <QTextStream>
#include <QDir>
int main(void) {
QTextStream out{stdout};
out << "Current path:" << QDir::currentPath() << endl;
out << "Home path:" << QDir::homePath() << endl;
out << "Temporary path:" << QDir::tempPath() << endl;
out << "Rooth path:" << QDir::rootPath() << endl;
return 0;
}
这个示例输出四个特殊路径。
out << "Current path:" << QDir::currentPath() << endl;
使用 QDir::currentPath 方法获取当前工作目录。
out << "Home path:" << QDir::homePath() << endl;
使用 QDir::homePath 方法返回主目录。
out << "Temporary path:" << QDir::tempPath() << endl;
使用 QDir::tempPath 方法获取临时目录。
out << "Rooth path:" << QDir::rootPath() << endl;
使用 QDir::rootPath 方法返回根目录。
$ ./specialpaths
Current path:/home/janbodnar/Documents/prog/qt5/files/specialpaths
Home path:/root
Temporary path:/tmp
Rooth path:/
Qt5 文件路径
一个文件由其名称和路径标识,路径由文件名、基本名称和后缀组成。
file_path.cpp
#include <QTextStream>
#include <QFileInfo>
int main(int argc, char *argv[]) {
QTextStream out{stdout};
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;
return 0;
}
在这个例子中,我们使用了多个方法来打印给定文件名的文件路径及其各部分。
QFileInfo fileinfo{filename};
使用QFileInfo类来标识文件路径。
QString absPath = fileinfo.absoluteFilePath();
absoluteFilePath方法返回包括文件名在内的绝对路径。
QString baseName = fileinfo.baseName();
baseName方法返回基本名称——文件名不包含路径部分的名称。
QString compBaseName = fileinfo.completeBaseName();
completeBaseName方法返回完整的基本名称——文件名中最后一个点字符之前的所有字符。
QString fileName = fileinfo.fileName();
fileName方法返回文件名,即基本名称和扩展名。
QString suffix = fileinfo.suffix();
suffix方法返回文件扩展名,即文件名中最后一个点字符之后的所有字符。
QString compSuffix = fileinfo.completeSuffix();
文件扩展名可能由几部分组成;
completeSuffix方法返回第一个点字符之后的所有字符。
$ ./filepath ~/Downloads/qt-everywhere-opensource-src-5.5.1.tar.gz
Absolute file path: /home/janbodnar/Downloads/qt-everywhere-opensource-src-5.5.1.tar.gz
Base name: qt-everywhere-opensource-src-5
Complete base name: qt-everywhere-opensource-src-5.5.1.tar
File name: qt-everywhere-opensource-src-5.5.1.tar.gz
Suffix: gz
Whole suffix: 5.1.tar.gz
Qt5文件权限
文件系统中的文件有一套保护机制。文件被赋予标志,确定谁可以访问和修改它们。
QFile::permissions方法返回文件的枚举OR标志。
permissions.cpp
#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];
auto 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;
return 0;
}
此示例为给定的文件生成类Unix的权限列表。有三种可能的用户类型:拥有者、文件所属的组和其余用户,称为其他人。前三个位置属于文件所有者,接下来的三个位置属于文件组,最后三个字符属于其他人。有四种权限:读取(r)、写入或修改(w)、执行(x)和没有权限(-)。
auto ps = QFile::permissions(filename);
使用QFile::permissions方法,获取权限枚举。
QString fper;
根据给定权限动态构建字符串。
if (ps & QFile::ReadOwner) {
fper.append('r');
} else {
fper.append('-');
}
使用&运算符确定返回的枚举是否包含QFile::ReadOwner标志。
$ ./permissions Makefile
rw-rw-r--
文件的所有者和所属用户组有读取和修改文件的权限。其他用户有读取文件的权限。由于文件不是可执行文件,因此没有执行文件的权限。
Qt5列出目录内容
在下面的示例中,我们显示给定目录的内容。
list_dir.cpp
#include <QTextStream>
#include <QFileInfo>
#include <QDir>
int main(int argc, char *argv[]) {
QTextStream out{stdout};
if (argc != 2) {
qWarning("Usage: list_dir directory");
return 1;
}
QString directory = argv[1];
QDir dir{directory};
if (!dir.exists()) {
qWarning("The directory does not exist");
return 1;
}
dir.setFilter(QDir::Files | QDir::AllDirs);
dir.setSorting(QDir::Size | QDir::Reversed);
QFileInfoList list = dir.entryInfoList();
int max_size = 0;
for (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;
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;
}
为了列出目录的内容,我们使用QDir类及其entryInfoList方法。文件列表按大小的相反顺序排序,并整齐地排列。有两列; 第一列包含文件名,第二列包含文件大小。
QDir dir{directory};
创建一个具有给定目录名称的QDir对象。
dir.setFilter(QDir::Files | QDir::AllDirs);
setFilter方法指定entryInfoList方法应返回的文件类型。
dir.setSorting(QDir::Size | QDir::Reversed);
setSorting方法指定entryInfoList方法使用的排序顺序。
QFileInfoList list = dir.entryInfoList();
entryInfoList方法返回目录中所有文件和目录的QFileInfo对象列表,根据过滤和排序方法进行过滤和排序。 QFileInfoList是QList<QFileInfo>的同义词。
for (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;
}
我们遍历文件列表并打印它们的名称和尺寸。第一列是左对齐的,必要时用空格填充;第二列是简单地附加到行尾。
$ ./list_dir .
Filename Bytes
list_dir.pro 291
list_dir.cpp 1092
.. 4096
. 4096
list_dir.o 10440
list_dir 19075
Makefile 28369
这是该例子的输出样本。
在本章中,我们在Qt5中处理了文件和目录。