ZetCode

Qt5 文件和目录

最后修改于 2023 年 10 月 18 日

在 Qt5 C++ 编程教程的这一部分,我们将使用文件和目录。

QFile, QDirQFileInfo 是在 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;
}

文件的大小由 QFileInfosize 方法确定。

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;
}

该示例从 words.txt 文件中读取数据。

QFile f{"words.txt"};

创建 QFile 对象的一个实例。 当 QFile 超出范围时,它会自动关闭文件。

if (!f.open(QIODevice::ReadOnly)) {

  qWarning("Cannot open file for reading");
  return 1;
}

QFileopen 方法以只读模式打开文件。 如果该方法失败,我们会发出警告并终止程序。

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

Qt5 复制文件

当我们复制文件时,我们会使用不同的名称或在文件系统中的不同位置创建一个文件的精确副本。

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) {

    qWarning("Usage: copyfile source destination");
    return 1;
}

该程序采用两个参数; 如果没有给出,它将以警告消息结束。

QString src = argv[1];

从程序的命令行参数中,我们获取源文件的名称。

if (!QFile{src}.exists()) {

    qWarning("The source file does not exist");
    return 1;
}

我们使用 QFileexists 方法检查源文件是否存在。 如果它不存在,我们使用警告消息终止程序。

QString dest(argv[2]);

我们获取目标文件名。

QFile::copy(src, dest);

源文件使用 QFile::copy 方法复制。 第一个参数是源文件名,第二个参数是目标文件名。

Qt5 文件所有者和组

每个文件都有一个作为其所有者的用户。 文件还属于一组用户,以便更好地管理和保护文件。

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();

文件的主要组由 QFileInfogroup 方法确定。

QString owner = fileinfo.owner();

文件的所有者由 QFileInfoowner 方法确定。

$ 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;
}

该示例打印上次读取和上次修改的时间。

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 对象列表,这些对象根据过滤和排序方法进行过滤和排序。 QFileInfoListQList<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 中使用了文件和目录。