1. Qt文件系统基础:QDir、QFileInfo、QFile的基本使用与文件路径操作
文件操作,说白了就是每个Qt应用的必修课。不管你是做文本编辑器、图片浏览器,还是搞什么数据分析工具,总得跟文件打交道。我这些年带团队做项目,发现很多新手甚至老手,在文件路径和内存管理上栽过跟头。嗯,这一章我们先打好基础,把QDir、QFileInfo、QFile这三个核心类吃透。
1.1 文件路径操作:别让路径坑了你
先聊聊路径。Qt里路径分两种:绝对路径和相对路径。绝对路径从根目录开始写,比如 C:/Users/John/Documents/test.txt。相对路径是相对于当前工作目录的,比如 ./data/config.ini。
/,别用Windows的反斜杠 \。反斜杠在C++字符串里是转义符,写起来麻烦还容易出错。我见过有人写 "C:\\Users\\test",结果字符串解析出问题,排查了半天。
我个人习惯用 QDir::toNativeSeparators() 做转换,这样代码跨平台时不用改。来看个例子:
#include <QDir>
#include <QDebug>
int main() {
// 获取当前目录
QString currentPath = QDir::currentPath();
qDebug() << "当前目录:" << currentPath;
// 拼接路径
QString filePath = QDir::currentPath() + "/data/config.ini";
qDebug() << "文件路径:" << filePath;
// 转换为原生格式
QString nativePath = QDir::toNativeSeparators(filePath);
qDebug() << "原生路径:" << nativePath;
return 0;
}
你想想看,如果项目要同时跑在Windows和Linux上,路径格式不一致就会出问题。用 QDir::toNativeSeparators() 就省心了。
1.2 QDir:目录操作的瑞士军刀
QDir能干的事太多了。创建目录、遍历文件、过滤类型、判断是否存在……我把它常用的功能整理成了一张表:
| 方法 | 功能 | 返回值 |
|---|---|---|
exists() |
判断目录是否存在 | bool |
mkdir() |
创建单级目录 | bool |
mkpath() |
创建多级目录 | bool |
entryList() |
获取目录下的条目列表 | QStringList |
remove() |
删除空目录 | bool |
rmdir() |
删除目录(递归删除需自己实现) | bool |
这里有个坑:remove() 只能删空目录。我曾经在项目里直接调 remove() 删一个非空目录,结果返回 false,日志里也没报错,排查了好久才发现是目录里还有文件。后来我写了个递归删除函数:
bool removeDirectoryRecursively(const QString &dirPath) {
QDir dir(dirPath);
if (!dir.exists()) return false;
// 先删文件
for (const QFileInfo &info : dir.entryInfoList(QDir::Files)) {
QFile::remove(info.absoluteFilePath());
}
// 再递归删子目录
for (const QFileInfo &info : dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
removeDirectoryRecursively(info.absoluteFilePath());
}
// 最后删自己
return dir.rmdir(dirPath);
}
QDir::NoDotAndDotDot 过滤掉 . 和 ..,不然递归会死循环。我第一次写递归遍历时就踩了这个坑,程序直接崩了。
1.3 QFileInfo:文件信息的百科全书
QFileInfo 不直接操作文件,它只负责查信息。文件大小、修改时间、权限、后缀名……这些都能查。我经常用它来做文件合法性校验。
#include <QFileInfo>
QFileInfo info("/home/user/data/report.pdf");
if (info.exists()) {
qDebug() << "文件名:" << info.fileName(); // report.pdf
qDebug() << "基础名:" << info.baseName(); // report
qDebug() << "后缀名:" << info.suffix(); // pdf
qDebug() << "大小:" << info.size() << "字节";
qDebug() << "最后修改:" << info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
qDebug() << "是否可读:" << info.isReadable();
qDebug() << "是否可写:" << info.isWritable();
}
为什么说它重要?因为很多文件操作失败,根源在于信息没查清楚。比如你要读一个文件,先查 isReadable(),如果返回 false,就别硬读了,直接提示用户权限不足。我在做日志分析工具时就遇到过,用户给了一个只读文件,程序直接崩溃,后来加了 isReadable() 检查才解决。
1.4 QFile:读写文件的正确姿势
QFile 是文件读写的核心类。它支持文本模式和二进制模式。我建议你养成一个习惯:用 QFile 配合 QTextStream 或 QDataStream,别自己手动处理编码和字节序。
来看一个文本文件读写的标准示例:
#include <QFile>
#include <QTextStream>
// 写文件
QFile file("output.txt");
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << "Hello, Qt!" << "\n";
out << "这是第二行" << "\n";
file.close();
} else {
qWarning() << "打开文件失败:" << file.errorString();
}
// 读文件
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
qDebug() << line;
}
file.close();
}
close()。虽然QFile的析构函数会自动关闭,但显式关闭能让你更早发现资源泄漏。
二进制文件读写用 QDataStream:
QFile binFile("data.bin");
if (binFile.open(QIODevice::WriteOnly)) {
QDataStream out(&binFile);
out << 42; // 写入整数
out << 3.14; // 写入浮点数
out << QString("Qt"); // 写入字符串
binFile.close();
}
1.5 知识体系总览
下面这张图把本章的核心知识点串起来了。你可以把它当作一个快速索引:
1.6 避坑指南
最后分享几个我踩过的坑,你遇到了能少走弯路:
- 路径编码问题:Windows下中文路径可能乱码。用
QDir::toNativeSeparators()和QString::fromLocal8Bit()处理。 - 文件未关闭导致资源泄漏:打开文件后如果异常退出,文件句柄不会自动释放。建议用RAII思想,或者把
close()写在析构函数里。 - 权限不足:写文件前先检查
isWritable(),读文件前检查isReadable()。别等报错了再回头查。 - 递归遍历死循环:遍历目录时一定要过滤
.和..,不然递归会无限循环。
close(),然后再写中间的逻辑。这样就不会忘记关闭了。你试试看,很管用。