优秀的编程知识分享平台

网站首页 > 技术文章 正文

Qt5 入门教程-第5章 文件和目录

nanyue 2025-05-22 12:29:18 技术文章 3 ℃

大道至简,在 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("用法: file_size file");
    return 1;
  }

  QString filename = argv[1];

  QFile f{filename};

  if (!f.exists()) {

    qWarning("该文件不存在");
    return 1;
  }

  QFileInfo fileinfo{filename};

  qint64 size = fileinfo.size();

  QString str = "文件大小为: %1 字节";

  out << str.arg(size) << endl;

  return 0;
}

文件的大小是通过 QFileInfo 的 size 方法来确定的。

QString filename = argv[1];

文件的名称作为参数传递给程序。

QFile f{filename};

if (!f.exists()) {

  qWarning("该文件不存在");
  return 1;
}

通过 QFile 类的 exists 方法来检查文件是否存在。如果文件不存在,我们发出警告并终止程序。

QFileInfo fileinfo{filename};

创建一个 QFileInfo 的实例。

qint64 size = fileinfo.size();

使用 size 方法来确定文件大小。qint64 是一种在 Qt 支持的所有平台上都保证为 64 位的类型。

QString str = "文件大小为: %1 字节";

out << str.arg(size) << endl;

上面实列 将结果打印到控制台,如下所示:。

$ ./filesize Makefile
文件大小为: 19993 字节

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("无法打开文件进行读取");
    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("无法打开文件进行读取");
  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("无法打开文件");
  }

  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("用法: copyfile source destination");
      return 1;
  }

  QString src = argv[1];

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

      qWarning("源文件不存在");
      return 1;
  }

  QString dest(argv[2]);

  QFile::copy(src, dest);

  return 0;
}

这个示例使用 QFile::copy 方法创建给定文件的副本。

if (argc != 3) {

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

该程序接受两个参数;如果没有提供这两个参数,程序将以警告消息结束。

QString src = argv[1];

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

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

    qWarning("源文件不存在");
    return 1;
}

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

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("用法: owner file");
      return 1;
  }

  QString filename = argv[1];

  QFileInfo fileinfo{filename};

  QString group = fileinfo.group();
  QString owner = fileinfo.owner();

  out << "用户组: " << group << endl;
  out << "所有者: " << owner << endl;

  return 0;
}

这个示例打印给定文件的所有者和主用户组。

QFileInfo fileinfo{filename};

创建一个 QFileInfo 类的实例。它的参数是作为命令行参数给出的文件名。

QString group = fileinfo.group();

使用 QFileInfo 的 group 方法确定文件的主用户组。

QString owner = fileinfo.owner();

使用 QFileInfo 的 owner 方法确定文件的所有者。

$ touch myfile
$ ./ownergroup myfile
用户组: janbodnar
所有者: 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("用法: file_times file");
      return 1;
  }

  QString filename = argv[1];

  QFileInfo fileinfo{filename};

  QDateTime last_rea = fileinfo.lastRead();
  QDateTime last_mod = fileinfo.lastModified();

  out << "最后读取时间: " << last_rea.toString() << endl;
  out << "最后修改时间: " << last_mod.toString() << endl;

  return 0;
}

这个示例打印文件的最后读取时间和最后修改时间。

QFileInfo fileinfo{filename};

创建一个 QFileInfo 对象。

QDateTime last_rea = fileinfo.lastRead();

lastRead 方法返回文件最后一次被读取(访问)的日期和时间。

QDateTime last_mod = fileinfo.lastModified();

lastModified 方法返回文件最后一次被修改的日期和时间。

$ ./filetimes Makefile 
最后读取时间: Sun Dec 6 12:46:11 2020
最后修改时间: 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目录成功创建" << endl;
  }

  dir.mkdir("mydir2");

  if (dir.exists("mydir2")) {

    dir.rename("mydir2", "newdir");
  }

  dir.mkpath("temp/newdir");

  return 0;
}

在这个示例中,我们展示了四种处理目录的方法。

if (dir.mkdir("mydir")) {

  out << "mydir目录成功创建" << 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 << "当前路径:" << QDir::currentPath() << endl;
  out << "主目录路径:" << QDir::homePath() << endl;
  out << "临时路径:" << QDir::tempPath() << endl;
  out << "根目录路径:" << QDir::rootPath() << endl;
  return 0;
}

这个示例打印四个特殊路径。

out << "当前路径:" << QDir::currentPath() << endl;

使用 QDir::currentPath 方法检索当前工作目录。

out << "主目录路径:" << QDir::homePath() << endl;

使用 QDir::homePath 方法返回主目录。

out << "临时路径:" << QDir::tempPath() << endl;

使用 QDir::tempPath 方法检索临时目录。

out << "根目录路径:" << QDir::rootPath() << endl;

使用 QDir::rootPath 方法返回根目录。

$ ./specialpaths 
当前路径:/home/janbodnar/Documents/prog/qt5/files/specialpaths
主目录路径:/root
临时路径:/tmp
根目录路径:/

Qt5 文件路径

一个文件由其名称和路径来标识;路径由文件名、基本名称和后缀组成。

file_path.cpp

#include <QTextStream>
#include <QFileInfo>

int main(int argc, char *argv[]) {

  QTextStream out{stdout};

  if (argc != 2) {

      out << "用法: 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 << "绝对文件路径: " << absPath << endl;
  out << "基本名称: " << baseName << endl;
  out << "完整基本名称: " << compBaseName << endl;
  out << "文件名: " << fileName << endl;
  out << "后缀: " << suffix << endl;
  out << "完整后缀: " << 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
绝对文件路径: /home/janbodnar/Downloads/qt-everywhere-opensource-src-5.5.1.tar.gz
基本名称: qt-everywhere-opensource-src-5
完整基本名称: qt-everywhere-opensource-src-5.5.1.tar
文件名: qt-everywhere-opensource-src-5.5.1.tar.gz
后缀: gz
完整后缀: 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 << "用法: 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("用法: list_dir directory");
      return 1;
  }

  QString directory = argv[1];

  QDir dir{directory};

  if (!dir.exists()) {

      qWarning("该目录不存在");
      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

Tags:

最近发表
标签列表