ZetCode

在 Qt4 中使用文件和目录

最后修改于 2023 年 10 月 18 日

在本 Qt4 C++ 编程教程中,我们将使用文件和目录。

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

文件大小

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

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

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

QString filename = argv[1];

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

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

结果将打印到控制台。

$ ./file_size Makefile 
The size is: 7483 bytes

这是示例的输出。

读取文件内容

为了读取文件的内容,我们首先必须以读取模式打开文件。然后创建一个输入文件流;从这个流中,数据被读取。

read_file.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 文件中读取数据。该文件包含八种颜色的名称。

QFile file("colours");

创建一个 QFile 对象的实例。

if (!file.open(QIODevice::ReadOnly)) {
    
  qWarning("Cannot open file for reading");
  return 1;
}

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

QTextStream in(&file);

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

while (!in.atEnd()) {
    
  QString line = in.readLine();    
  out << line << endl;
}

在 while 循环中,我们逐行读取文件,直到文件末尾。如果流中没有更多数据要读取,atEnd 方法将返回 true。readLine 方法从流中读取一行。

file.close();

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

$ ./read_file colours 
red
green
blue
yellow
brown
white
black
orange

这是示例的输出。

写入文件

为了写入文件,我们以写入模式打开文件,创建一个指向该文件的输出流,并使用写入运算符写入该流。

write2file.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 的文件。

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

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

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

使用 open 方法,我们以只写模式打开文件。

QTextStream out(&file);

这行代码创建一个 QTextStream,它操作文件句柄。换句话说,要写入的数据流将指向该文件。

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

数据使用 << 运算符写入。

file.close(); 

最后,文件句柄被关闭。

$ ./write2file 
$ cat distros 
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 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 方法创建所提供文件的副本。

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

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

QString destin(argv[2]);

我们获取目标文件名。

QFile::copy(source, destin);

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

文件所有者和组

每个文件都有一个用户,该用户是它的所有者。一个文件也属于一组用户,以便更好地管理和保护文件。

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

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

QFileInfo fileinfo(filename);

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

QString group = fileinfo.group();

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

QString owner = fileinfo.owner();

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

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

新创建的文件会自动获得用户默认组。

最后读取,最后修改

文件存储有关上次读取或修改它们的时间的信息。要获取此信息,我们使用 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;
}

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

QFileInfo fileinfo(filename);

创建 QFileInfo 对象。

QDateTime last_rea = fileinfo.lastRead();

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

QDateTime last_mod = fileinfo.lastModified();

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

$ ./file_times Makefile 
Last read: Mon Oct 19 10:23:54 2015
Last modified: Mon Oct 19 10:23:33 2015

时间可能不同,也可能相同。

使用目录

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

在这个例子中,我们介绍了四种处理目录的方法。

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 一次创建一个新目录和所有必要的父目录。

特殊路径

文件系统中存在一些特殊路径;例如,一个主目录或一个根目录。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;
}

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

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 方法返回。

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

这是一个示例输出。

文件路径

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

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

在这个例子中,我们使用几种方法来打印文件路径及其给定文件名的各个部分。

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 方法返回文件中第一个点字符之后(但不包括)的所有字符。

$ ./file_path ~/Downloads/qt-everywhere-opensource-src-4.8.7.tar.gz
Absolute file path: /home/janbodnar/Downloads/qt-everywhere-opensource-src-4.8.7.tar.gz
Base name: qt-everywhere-opensource-src-4
Complete base name: qt-everywhere-opensource-src-4.8.7.tar
File name: qt-everywhere-opensource-src-4.8.7.tar.gz
Suffix: gz
Whole suffix: 8.7.tar.gz

这是程序的输出。

权限

文件系统中的文件具有保护系统。文件被赋予标志,这些标志决定了谁可以访问和修改它们。QFile::permissions 方法返回相关文件的 OR-ed 标志的枚举。

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];
  
  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) 和无权限 (-)。

QFile::Permissions 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--

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

列出目录内容

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

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;
  
  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 方法。文件列表按其大小进行反向排序并整齐地排列。有两列;第一列包含文件名,第二列包含文件大小。

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> 的同义词。

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

我们遍历文件列表并打印它们的文件名和大小。第一列向左对齐,并根据需要用空格填充;第二列只是附加到该行的末尾。

$ ./list_dir .
Filename      Bytes
list_dir.pro  302
list_dir.cpp  1092
..            4096
.             4096
Makefile      7456
list_dir.o    8848
list_dir      14687

这是该示例的示例输出。

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