# 在 Qt5 中处理文件和目录

> 原文： [http://zetcode.com/gui/qt5/files/](http://zetcode.com/gui/qt5/files/)

在 Qt5 C++ 编程教程的这一部分中，我们处理文件和目录。

`QFile`，`QDir`和`QFileInfo`是在 Qt5 中处理文件的基本类。 `QFile`提供了用于读取和写入文件的接口。 `QDir`提供对目录结构及其内容的访问。 `QFileInfo`提供与系统无关的文件信息，包括文件名和在文件系统中的位置，访问时间和修改时间，权限或文件所有权。

## 文件大小

在下一个示例中，我们确定文件的大小。

`file_size.cpp`

```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];

  if (!QFile(filename).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;
}

```

文件的大小由`QFileInfo`的`size()`方法确定。

```cpp
QString filename = argv[1];

```

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

```cpp
if (!QFile(filename).exists()) {
  qWarning("The file does not exist");
  return 1;
}

```

使用`QFile`类的`exists()`方法检查文件的存在。 如果不存在，我们将发出警告并终止程序。

```cpp
QFileInfo fileinfo(filename);

```

创建`QFileInfo`的实例。

```cpp
qint64 size = fileinfo.size();

```

文件大小由`size()`方法确定。 `qint64`是在 Qt 支持的所有平台上保证为 64 位的类型。

```cpp
QString str = "The size is: %1 bytes";

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

```

结果将打印到控制台。

输出：

```cpp
$ ./file_size Makefile 
The size is: 28029 bytes

```

## 读取文件内容

为了读取文件的内容，我们必须首先打开文件进行读取。 然后创建一个输入文件流； 从该流中读取数据。

`read_file.cpp`

```cpp
#include <QTextStream>
#include <QFile>

int main(void) {

  QTextStream out(stdout);

  QFile file("colours");

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

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

  QTextStream in(&file);

  while (!in.atEnd()) {

    QString line = in.readLine();    
    out << line << endl;
  }

  file.close();
}

```

该示例从`colours`文件读取数据。 该文件包含八种颜色的名称。

```cpp
QFile file("colours");

```

创建`QFile`对象的实例。

```cpp
if (!file.open(QIODevice::ReadOnly)) {

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

```

`QFile`的`open()`方法以只读模式打开文件。 如果该方法失败，我们将发出警告并终止程序。

```cpp
QTextStream in(&file);

```

创建输入流。 `QTextStream`接收文件句柄。 将从该流中读取数据。

```cpp
while (!in.atEnd()) {

  QString line = in.readLine();    
  out << line << endl;
}

```

在`while`循环中，我们逐行读取文件，直到文件结束。 如果没有更多数据要从流中读取，则`atEnd()`方法返回`true`。 `readLine()`方法从流中读取一行。

```cpp
file.close();

```

`close()`方法刷新数据并关闭文件句柄。

输出：

```cpp
$ ./read_file colours 
red
green
blue
yellow
brown
white
black
violet

```

## 写入文件

为了写入文件，我们在写入模式下打开文件，创建定向到该文件的输出流，并使用写入运算符写入该流。

`write2file.cpp`

```cpp
#include <QTextStream>
#include <QFile>

int main(void) {

  QTextStream out(stdout);

  QString filename = "distros";
  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(); 
}

```

该示例将五个 Linux 发行版的名称写入名为`distros`的文件名。

```cpp
QString filename = "distros";
QFile file(filename);

```

使用提供的文件名创建`QFile`对象。

```cpp
if (file.open(QIODevice::WriteOnly)) {

```

使用`open()`方法，我们以只写方法打开文件。

```cpp
QTextStream out(&file);

```

该行创建一个在文件句柄上运行的`QTextStream`。 换句话说，要写入的数据流被定向到文件。

```cpp
out << "Xubuntu" << endl;
out << "Arch" << endl;
out << "Debian" << endl;
out << "Redhat" << endl;
out << "Slackware" << endl;

```

数据通过`<<`运算符写入。

```cpp
file.close(); 

```

最后，文件句柄被关闭。

输出：

```cpp
$ ./write2file 
$ cat distros 
Xubuntu
Arch
Debian
Redhat
Slackware

```

## 复制文件

复制文件时，我们将使用文件名或文件系统的不同位置来精确复制该文件。

`copy_file.cpp`

```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 source = argv[1];

  if (!QFile(source).exists()) {
      qWarning("The source file does not exist");
      return 1;
  }

  QString destin(argv[2]);

  QFile::copy(source, destin);
}

```

该示例使用`QFile::copy()`方法创建提供的文件的副本。

```cpp
if (argc != 3) {

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

```

该程序有两个参数。 如果未给出，则以警告消息结尾。

```cpp
QString source = argv[1];

```

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

```cpp
if (!QFile(source).exists()) {
    qWarning("The source file does not exist");
    return 1;
}

```

我们使用`QFile`的`exists()`方法检查源文件是否存在。 如果它不存在，我们将以警告消息终止该程序。

```cpp
QString destin(argv[2]);

```

我们得到目标文件名。

```cpp
QFile::copy(source, destin);

```

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

## 文件所有者和组

每个文件都有一个拥有者的用户。 文件也属于一组用户，以更好地管理和保护文件。

`owner.cpp`

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

```

该示例打印给定文件的所有者和主要组。

```cpp
QFileInfo fileinfo(filename);

```

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

```cpp
QString group = fileinfo.group();

```

文件的主要组是通过`QFileInfo`的`group()`方法确定的。

```cpp
QString owner = fileinfo.owner();

```

文件的所有者通过`QFileInfo`的`owner()`方法确定。

输出：

```cpp
$ touch myfile
$ ./owner myfile 
Group: janbodnar
Owner: janbodnar

```

## 上次读取，上次修改

文件存储有关上次读取或修改它们的信息。 要获取此信息，我们使用`QFileInfo`类。

`file_times.cpp`

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

```

该示例打印给定文件的最后读取时间和最后修改时间。

```cpp
QFileInfo fileinfo(filename);

```

`QFileInfo`对象已创建。

```cpp
QDateTime last_rea = fileinfo.lastRead();

```

`lastRead()`方法返回上次读取（访问）文件的日期和时间。

```cpp
QDateTime last_mod = fileinfo.lastModified();

```

`lastModified()`方法返回上次修改文件的日期和时间。

输出：

```cpp
$ ./file_times Makefile 
Last read: Sun Nov 1 17:54:31 2015
Last modified: Sun Nov 1 17:54:30 2015

```

## 处理目录

`QDir`类具有用于处理目录的方法。

`dirs.cpp`

```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");
}

```

在示例中，我们提供了四种使用目录的方法。

```cpp
if (dir.mkdir("mydir")) {
  out << "mydir successfully created" << endl;
}

```

`mkdir()`方法创建一个目录。 如果目录创建成功，则返回`true`。

```cpp
if (dir.exists("mydir2")) {
  dir.rename("mydir2", "newdir");    
}

```

`exists()`检查目录是否存在。 `rename()`方法重命名目录。

```cpp
dir.mkpath("temp/newdir");

```

`mkpath()`一键创建一个新目录和所有必要的父目录。

## 特殊路径

文件系统中有一些特殊的路径。 例如主目录或根目录。 `QDir`类用于获取系统中的特殊路径。

`special_paths.cpp`

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

```

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

```cpp
out << "Current path:" << QDir::currentPath() << endl;

```

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

```cpp
out << "Home path:" << QDir::homePath() << endl;

```

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

```cpp
out << "Temporary path:" << QDir::tempPath() << endl;

```

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

```cpp
out << "Rooth path:" << QDir::rootPath() << endl;

```

根目录通过`QDir::rootPath()`方法返回。

输出：

```cpp
$ ./special_paths 
Current path:/home/janbodnar/prog/qt4/files/special_paths
Home path:/home/janbodnar
Temporary path:/tmp
Rooth path:/

```

## 文件路径

文件由文件名和路径标识。 路径由文件名，基本名和后缀组成。

`file_path.cpp`

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

```

在示例中，我们使用几种方法来打印文件路径及其给定文件名的一部分。

```cpp
QFileInfo fileinfo(filename);

```

文件路径是使用`QFileInfo`类标识的。

```cpp
QString absPath = fileinfo.absoluteFilePath();

```

`absoluteFilePath()`方法返回包含文件名的绝对路径。

```cpp
QString baseName = fileinfo.baseName();

```

`baseName()`方法返回基本名称-没有路径的文件名称。

```cpp
QString compBaseName = fileinfo.completeBaseName();

```

`completeBaseName()`方法返回完整的基本名称-文件中的所有字符，直到（但不包括）最后一个点字符。

```cpp
QString fileName = fileinfo.fileName();

```

`fileName()`方法返回文件名，该文件名是基本名称和扩展名。

```cpp
QString suffix = fileinfo.suffix();

```

`suffix()`方法返回文件结尾，该结尾由文件中所有字符组成，该文件之后（但不包括）最后一个点字符。

```cpp
QString compSuffix = fileinfo.completeSuffix();

```

文件结尾可能由几部分组成。 `completeSuffix()`方法返回第一个点字符之后（但不包括）后的文件中的所有字符。

输出：

```cpp
$ ./file_path ~/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

```

## 权限

文件系统中的文件具有保护系统。 文件带有标志，这些标志确定谁可以访问和修改它们。 `QFile::permissions()`方法返回有关文件的 OR-ED 标志的枚举。

`permissions.cpp`

```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];

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

```

该示例为给定文件生成类似 Unix 的权限列表。 有几种可能的用户类型：所有者，文件所属的组以及其余的称为其他用户。 前三个位置属于文件的所有者，后三个位置属于文件的组，后三个字符属于其他字符。 权限共有四种：读取（`r`），写入或修改（`w`），执行（`x`）和无权限（`-`）。

```cpp
QFile::Permissions ps = QFile::permissions(filename);

```

通过`QFile::permissions()`方法，我们获得了权限标志的枚举。

```cpp
QString fper;

```

该字符串是根据给定的权限动态构建的。

```cpp
if (ps & QFile::ReadOwner) {
    fper.append('r');
} else {
    fper.append('-');
}

```

我们使用&运算符确定返回的枚举是否包含`QFile::ReadOwner`标志。

输出：

```cpp
$ ./permissions Makefile 
rw-rw-r--

```

文件所属的所有者和用户组有权读取和修改文件。 其他用户有权读取该文件。 由于该文件不是可执行文件，因此没有执行该文件的权限。

## 列出目录内容

在下面的示例中，我们显示给定目录的内容。

`list_dir.cpp`

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

  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;

  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()`方法。 文件列表按其大小反向排序，并且排列整齐。 有两列； 第一列包含文件名，第二列包含文件大小。

```cpp
QDir dir(directory);

```

创建具有给定目录名称的`QDir`对象。

```cpp
dir.setFilter(QDir::Files | QDir::AllDirs);

```

`setFilter()`方法指定`entryInfoList()`方法应返回的文件类型。

```cpp
dir.setSorting(QDir::Size | QDir::Reversed);

```

`setSorting()`方法指定`entryInfoList()`方法使用的排序顺序。

```cpp
QFileInfoList list = dir.entryInfoList();

```

`entryInfoList()`方法返回目录中所有文件和目录的`QFileInfo`对象的列表，并通过过滤和排序方法进行过滤和排序。 `QFileInfoList`是`QList<QFileInfo>`的同义词。

```cpp
foreach (QFileInfo finfo, list) {

    QString name = finfo.fileName();
    int size = name.size();

    if (size > max_size) {

        max_size = size;
    }
}

```

我们遍历列表并确定最大文件名大小。 需要此信息来整齐地组织输出。

```cpp
int len = max_size + 2;

```

我们在列的长度上再加上两个空格。

```cpp
out << QString("Filename").leftJustified(len).append("Bytes") << endl;

```

在这里，我们打印列名。 `leftJustified()`方法返回给定大小的字符串，该字符串左对齐并在其右边用填充字符（默认为空格）填充。

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

```

我们浏览文件列表并打印它们的名称和大小。 第一列保持对齐，并在必要时用空格填充； 仅将第二列添加到该行的末尾。

输出：

```cpp
$ ./list_dir .
Filename      Bytes
list_dir.pro  291
list_dir.cpp  1092
..            4096
.             4096
list_dir.o    10440
list_dir      19075
Makefile      28369

```

这是示例的示例输出。

在本章中，我们使用文件和目录。