文件操作看似简单?深入Qt文件系统源码,解锁高性能IO的全部姿势
一、Qt文件系统概述
Qt提供了完整的跨平台文件系统抽象层,屏蔽了Windows/Linux/macOS的差异。核心类:
| 类名 | 用途 | 特点 |
|---|---|---|
| QFile | 文件读写 | 底层文件操作 |
| QFileInfo | 文件信息 | 元数据查询 |
| QDir | 目录操作 | 遍历、创建、删除 |
| QFileSystemWatcher | 文件监控 | 实时监听变化 |
| QSaveFile | 安全写入 | 原子性写入 |
| QTemporaryFile | 临时文件 | 自动清理 |
1.1 源码位置
qtbase/src/corelib/io/
├── qfile.cpp/h # QFile实现
├── qfiledevice.cpp/h # 文件设备基类
├── qfileinfo.cpp/h # 文件信息
├── qdir.cpp/h # 目录操作
├── qfilesystemwatcher.cpp/h # 文件监控
├── qsavefile.cpp/h # 安全写入
├── qtemporaryfile.cpp/h # 临时文件
└── qresource.cpp/h # 资源文件系统
二、QFile核心实现
2.1 QFile类结构
cpp
// qfile.h
class Q_CORE_EXPORT QFile : public QFileDevice
{
Q_OBJECT
public:
explicit QFile(const QString &name);
QFile(const QString &name, QObject *parent);
~QFile();
// 文件名操作
void setFileName(const QString &name);
QString fileName() const override;
// 打开模式
bool open(OpenMode mode) override;
bool open(FILE *fh, OpenMode mode, HandleFlags handleFlags = AutoCloseHandle);
bool open(int fd, OpenMode mode, HandleFlags handleFlags = AutoCloseHandle);
// 读写操作(继承自QFileDevice)
qint64 read(char *data, qint64 maxSize);
QByteArray read(qint64 maxSize);
QByteArray readAll();
qint64 write(const char *data, qint64 maxSize);
qint64 write(const QByteArray &data);
// 位置操作
qint64 pos() const;
bool seek(qint64 pos);
bool atEnd() const;
// 文件大小
qint64 size() const override;
bool resize(qint64 sz) override;
// 静态便捷函数
static bool exists(const QString &fileName);
static bool remove(const QString &fileName);
static bool rename(const QString &oldName, const QString &newName);
static bool copy(const QString &fileName, const QString &newName);
static QString symLinkTarget(const QString &fileName);
protected:
QFilePrivate *d_ptr;
};
2.2 打开模式详解
cpp
enum OpenModeFlag {
ReadOnly = 0x0001, // 只读
WriteOnly = 0x0002, // 只写
ReadWrite = ReadOnly | WriteOnly, // 读写
Append = 0x0004, // 追加模式
Truncate = 0x0008, // 清空文件
Text = 0x0010, // 文本模式(换行转换)
Unbuffered = 0x0020, // 无缓冲
NewOnly = 0x0040, // 仅创建新文件
ExistingOnly = 0x0080, // 仅打开已存在文件
};
2.3 高效读写示例
cpp
// 方式1:一次性读取全部(小文件)
QFile file("data.txt");
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
file.close();
}
// 方式2:分块读取(大文件)
QFile file("large.bin");
if (file.open(QIODevice::ReadOnly)) {
const qint64 chunkSize = 64 * 1024; // 64KB块
char buffer[64 * 1024];
while (!file.atEnd()) {
qint64 bytesRead = file.read(buffer, chunkSize);
if (bytesRead > 0) {
// 处理数据
processData(buffer, bytesRead);
}
}
file.close();
}
// 方式3:内存映射(超大文件,最高性能)
QFile file("huge.bin");
if (file.open(QIODevice::ReadOnly)) {
uchar *mapped = file.map(0, file.size());
if (mapped) {
// 直接访问内存,无需read调用
for (qint64 i = 0; i < file.size(); ++i) {
processByte(mapped[i]);
}
file.unmap(mapped);
}
file.close();
}
// 方式4:写入文件
QFile file("output.txt");
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file);
out << "Hello Qt!" << Qt::endl;
out << "File IO Demo" << Qt::endl;
file.close();
}
三、QFileInfo文件信息
3.1 核心接口
cpp
// qfileinfo.h
class Q_CORE_EXPORT QFileInfo
{
public:
QFileInfo(const QString &file);
// 文件属性
bool exists() const;
bool isFile() const;
bool isDir() const;
bool isSymLink() const;
bool isReadable() const;
bool isWritable() const;
bool isExecutable() const;
bool isHidden() const;
// 大小和时间
qint64 size() const;
QDateTime created() const;
QDateTime lastModified() const;
QDateTime lastRead() const;
// 路径分解
QString fileName() const; // 文件名(含扩展名)
QString baseName() const; // 基本名(不含扩展名)
QString completeBaseName() const;
QString suffix() const; // 扩展名
QString completeSuffix() const;
QString path() const; // 目录路径
QString absolutePath() const; // 绝对目录路径
QString absoluteFilePath() const;// 绝对完整路径
QString canonicalFilePath() const;// 规范化路径
// 权限
QFile::Permissions permissions() const;
bool permission(QFile::Permissions permissions) const;
// 所有者
QString owner() const;
uint ownerId() const;
QString group() const;
uint groupId() const;
};
3.2 实用示例
cpp
QFileInfo info("/home/user/documents/report.pdf");
qDebug() << "文件名:" << info.fileName(); // "report.pdf"
qDebug() << "基本名:" << info.baseName(); // "report"
qDebug() << "扩展名:" << info.suffix(); // "pdf"
qDebug() << "大小:" << info.size() << "bytes";
qDebug() << "修改时间:" << info.lastModified();
qDebug() << "绝对路径:" << info.absoluteFilePath();
// 检查文件类型
if (info.isDir()) {
qDebug() << "这是一个目录";
} else if (info.isSymLink()) {
qDebug() << "符号链接指向:" << info.symLinkTarget();
}
// 批量获取目录下文件信息
QDir dir("/home/user/documents");
QFileInfoList fileList = dir.entryInfoList(QDir::Files | QDir::Readable);
for (const QFileInfo &file : fileList) {
qDebug() << file.fileName() << file.size() << "bytes";
}
四、QDir目录操作
4.1 核心接口
cpp
// qdir.h
class Q_CORE_EXPORT QDir
{
public:
QDir(const QString &path);
// 目录导航
QString path() const;
void setPath(const QString &path);
QString absolutePath() const;
QString canonicalPath() const;
bool cd(const QString &dirName);
bool cdUp();
QString dirName() const;
// 目录内容
QStringList entryList(QDir::Filters filters = NoFilter,
QDir::SortFlags sort = NoSort) const;
QFileInfoList entryInfoList(QDir::Filters filters = NoFilter,
QDir::SortFlags sort = NoSort) const;
// 目录操作
bool mkdir(const QString &dirName);
bool rmdir(const QString &dirName);
bool mkpath(const QString &dirPath); // 创建完整路径
bool rmpath(const QString &dirPath); // 删除完整路径
// 静态便捷函数
static QStringList nameFiltersFromString(const QString &nameFilter);
static bool exists(const QString &path);
static bool isAbsolutePath(const QString &path);
static QString cleanPath(const QString &path);
static QString toNativeSeparators(const QString &pathName);
static QString fromNativeSeparators(const QString &pathName);
};
4.2 过滤和排序标志
cpp
// 过滤标志
enum Filter {
Dirs = 0x001, // 目录
Files = 0x002, // 文件
Drives = 0x004, // 驱动器
NoSymLinks = 0x008, // 排除符号链接
AllEntries = Dirs | Files | Drives,
Readable = 0x010, // 可读
Writable = 0x020, // 可写
Executable = 0x040, // 可执行
Modified = 0x080, // 已修改
Hidden = 0x100, // 隐藏文件
System = 0x200, // 系统文件
AllDirs = 0x400, // 包含隐藏目录
CaseSensitive = 0x800,
NoDotAndDotDot = 0x1000, // 排除 . 和 ..
NoDot = 0x2000,
NoDotDot = 0x4000,
};
// 排序标志
enum SortFlag {
Name = 0x00, // 按名称
Time = 0x01, // 按时间
Size = 0x02, // 按大小
Type = 0x03, // 按类型
Unsorted = 0x03,
SortByMask = 0x03,
DirsFirst = 0x04, // 目录优先
Reversed = 0x08, // 反向排序
IgnoreCase = 0x10, // 忽略大小写
DirsLast = 0x20,
LocaleAware = 0x40,
};
4.3 实用示例
cpp
// 遍历目录(递归)
void traverseDirectory(const QString &path, int depth = 0)
{
QDir dir(path);
// 先处理文件
QFileInfoList files = dir.entryInfoList(QDir::Files);
for (const QFileInfo &file : files) {
qDebug() << QString(depth * 2, ' ') << file.fileName();
}
// 再处理子目录
QFileInfoList dirs = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &subDir : dirs) {
qDebug() << QString(depth * 2, ' ') << "[" << subDir.fileName() << "]";
traverseDirectory(subDir.absoluteFilePath(), depth + 1);
}
}
// 按条件过滤
QDir dir("/home/user");
dir.setNameFilters(QStringList() << "*.cpp" << "*.h");
dir.setFilter(QDir::Files | QDir::Readable);
dir.setSorting(QDir::Time | QDir::Reversed); // 按时间倒序
QStringList sourceFiles = dir.entryList();
五、QFileSystemWatcher文件监控
5.1 实时监控文件变化
cpp
// filesystemwatcher_demo.cpp
#include <QFileSystemWatcher>
#include <QDebug>
class FileWatcher : public QObject
{
Q_OBJECT
public:
FileWatcher()
{
watcher = new QFileSystemWatcher(this);
connect(watcher, &QFileSystemWatcher::fileChanged,
this, &FileWatcher::onFileChanged);
connect(watcher, &QFileSystemWatcher::directoryChanged,
this, &FileWatcher::onDirectoryChanged);
}
void watchFile(const QString &path)
{
if (QFile::exists(path)) {
watcher->addPath(path);
qDebug() << "开始监控文件:" << path;
}
}
void watchDirectory(const QString &path)
{
QDir dir(path);
if (dir.exists()) {
watcher->addPath(path);
qDebug() << "开始监控目录:" << path;
}
}
private slots:
void onFileChanged(const QString &path)
{
qDebug() << "文件已修改:" << path;
// 文件可能被删除后重建,需要重新添加监控
if (!watcher->files().contains(path) && QFile::exists(path)) {
watcher->addPath(path);
}
}
void onDirectoryChanged(const QString &path)
{
qDebug() << "目录内容已变化:" << path;
}
private:
QFileSystemWatcher *watcher;
};
六、QSaveFile安全写入
6.1 原子性写入
cpp
// QSaveFile确保写入完整性
// 写入成功后原子性替换原文件
// 写入失败则原文件不受影响
bool saveConfig(const QString &path, const QByteArray &data)
{
QSaveFile file(path);
if (!file.open(QIODevice::WriteOnly)) {
return false;
}
// 写入数据
qint64 written = file.write(data);
if (written != data.size()) {
// 写入失败,自动删除临时文件
file.cancelWriting();
return false;
}
// 提交更改(原子性替换)
return file.commit();
}
七、异步文件操作
7.1 使用QtConcurrent
cpp
#include <QtConcurrent>
// 异步读取大文件
QFuture<QByteArray> readFileAsync(const QString &path)
{
return QtConcurrent::run([path]() {
QFile file(path);
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readAll();
file.close();
return data;
}
return QByteArray();
});
}
// 使用
QFuture<QByteArray> future = readFileAsync("large_file.bin");
// 非阻塞检查
if (future.isFinished()) {
QByteArray data = future.result();
}
// 或使用QFutureWatcher监控完成
QFutureWatcher<QByteArray> *watcher = new QFutureWatcher<QByteArray>();
connect(watcher, &QFutureWatcher<QByteArray>::finished, [watcher]() {
QByteArray data = watcher->result();
qDebug() << "读取完成,大小:" << data.size();
watcher->deleteLater();
});
watcher->setFuture(future);
八、性能优化技巧
8.1 最佳实践
cpp
// 1. 使用内存映射处理大文件
QFile file("huge.bin");
if (file.open(QIODevice::ReadOnly)) {
uchar *data = file.map(0, file.size());
// 直接内存访问,无需系统调用
file.unmap(data);
}
// 2. 批量写入减少IO次数
QFile file("output.bin");
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
out << largeVector; // 一次写入整个容器
// 3. 使用缓冲区
QFile file("data.bin");
file.open(QIODevice::WriteOnly);
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
// 先写入缓冲区,再一次性写入文件
// 4. 避免频繁查询文件信息
QFileInfo info(path); // 缓存info对象
// 多次使用info而不是多次调用QFileInfo(path)
九、总结
Qt文件系统的核心设计:
- 统一抽象:QFile/QDir/QFileInfo屏蔽平台差异
- 多种读写:分块读取、内存映射、流式操作
- 实时监控:QFileSystemWatcher监听文件变化
- 安全写入:QSaveFile保证原子性操作
《注:若有发现问题欢迎大家提出来纠正》