文件操作,是每一款桌面应用都绕不开的命根子。小到读写一段文本,大到管理复杂的配置文件、做数据持久化,背后都站着它。Qt作为跨平台开发框架,最拿得出手的优势之一,就是给了一套统一的文件操作API,把C/C++原生库和系统API之间的平台鸿沟一把填平,Linux下的open/read/write,Windows下的CreateFile,到了Qt这里,统统不用再操心。Qt诞生于1991年,比C++标准库的标准化进程还早,这套文件系统封装经过长期迭代优化,和QString、QByteArray这些Qt核心类无缝集成,用起来比C++17才引入的std::filesystem更顺手、更高效。
本文会把Qt文件操作的核心知识系统过一遍:从I/O设备的继承体系,到QFile类的完整API用法;从零实现一个支持打开和保存的简易记事本,再到用QFileInfo类获取文件属性。所有内容都配着完整可运行的代码,当作Qt入门的复习资料正合适,帮你快速捡回文件操作的核心逻辑,也顺手避开那些常见的坑。

目录
[1.1 Qt中的I/O设备继承体系](#1.1 Qt中的I/O设备继承体系)
[1.2 QFile的两个常用子类](#1.2 QFile的两个常用子类)
二、QFile核心API------文件如何打开、读取与写入
[2.1 QFile构造函数](#2.1 QFile构造函数)
[2.2 open():打开文件的基本方式](#2.2 open():打开文件的基本方式)
[2.3 从QFile中读取文件内容](#2.3 从QFile中读取文件内容)
[2.3.1 read():读取指定数量的数据](#2.3.1 read():读取指定数量的数据)
[2.3.2 readLine():按行读取文件](#2.3.2 readLine():按行读取文件)
[2.3.3 readAll():一次读取全部内容](#2.3.3 readAll():一次读取全部内容)
[2.4 写入文件:保存数据到磁盘](#2.4 写入文件:保存数据到磁盘)
[2.5 close():文件使用完成后的关闭操作](#2.5 close():文件使用完成后的关闭操作)
[3.1 从创建工程到搭建界面](#3.1 从创建工程到搭建界面)
[3.1.1 mainwindow.h:声明窗口功能](#3.1.1 mainwindow.h:声明窗口功能)
[3.1.2 mainwindow.cpp:实现窗口初始化](#3.1.2 mainwindow.cpp:实现窗口初始化)
[3.2 打开文件:将内容读取到编辑器](#3.2 打开文件:将内容读取到编辑器)
[3.3 保存文件:将编辑内容写回磁盘](#3.3 保存文件:将编辑内容写回磁盘)
[3.4 简易记事本运行效果](#3.4 简易记事本运行效果)
[4.1 QFileInfo构造函数](#4.1 QFileInfo构造函数)
[4.2 QFileInfo常用API](#4.2 QFileInfo常用API)
[4.3 通过代码获取文件信息](#4.3 通过代码获取文件信息)
[4.3.1 widget.h:声明文件信息相关功能](#4.3.1 widget.h:声明文件信息相关功能)
[4.3.2 widget.cpp:实现文件信息获取](#4.3.2 widget.cpp:实现文件信息获取)
[4.3.3 运行效果](#4.3.3 运行效果)
一、认识Qt文件系统------从I/O设备到QFile
Qt作为通用跨平台开发库,在文件系统这块给的能力相当完整,读写文件、获取文件信息、复制重命名、遍历目录,日常用得到的,它几乎全包了。
在Qt出现之前,文件操作基本靠两条路:一条是C语言标准库的fopen/fclose/fread/fwrite系列,另一条是操作系统的原生API。这两条路都有明显的短板。C标准库功能有限,拿不到文件的详细属性;系统API则完全不讲跨平台,Linux下写的代码,搬到Windows上直接歇菜。
Qt诞生于1991年,比C++标准的正式发布(1998年)还早。它的文件系统封装经过三十多年的迭代优化,不仅把跨平台问题解决得干干净净,还跟QString、QByteArray这些Qt核心类无缝集成。虽然C++17引入了std::filesystem,但Qt的文件操作API在易用性、功能完整性和与Qt生态的兼容性上,依然有明显优势。所以在Qt项目里,强烈推荐直接用Qt自己的文件操作类。
Qt文件操作的核心流程,跟原生方式完全一致,都是"打开 → 读/写 → 关闭"那一套。区别只在于,它把这些操作封装成了更顺手的类和方法,用起来省心不少。
1.1 Qt中的I/O设备继承体系
Qt里所有输入输出设备,追根溯源,都继承自同一个祖宗QIODevice。这个基类定下了所有I/O设备的通用接口:打开、关闭、读、写,一个不落。文件、网络套接字、串口、蓝牙、进程通信、内存缓冲区,统统算I/O设备,所以它们全都挂在QIODevice名下。完整的继承关系,长这样:
cpp
QObject(Qt 所有对象的基类)
└── QIODevice(所有 I/O 设备的基类)
├── QFileDevice(文件设备的基类)
│ ├── QFile(普通文件操作)
│ ├── QTemporaryFile(临时文件操作)
│ └── QSaveFile(安全写文件)
├── QAbstractSocket(网络套接字基类)
│ ├── QTcpSocket(TCP 套接字)
│ └── QUdpSocket(UDP 套接字)
├── QSerialPort(串口通信)
├── QBluetoothSocket(蓝牙通信)
├── QProcess(进程间通信,封装了 fork/exec 操作)
└── QBuffer(内存缓冲区操作)
其中,QFile是我们日常打交道最多的那个。它继承自QFileDevice,专门负责普通文件的读写,文件操作的门面担当。
1.2 QFile的两个常用子类
除了QFile本尊,Qt还备了两个非常实用的子类,专门收拾特定场景下的麻烦事。
QTemporaryFile:临时文件专业户。
它生来就是造临时文件的。最省心的一点是对象一销毁,对应的临时文件自动删除,片甲不留。你存完临时数据,拍拍屁股走人就行,不用惦记着手动清理。少写一行清理代码,就少埋一个忘记删文件的坑。
QSaveFile:安全写入的守门员。
它玩的是"先写临时文件,写完再替换原文件"的原子性策略。写数据的时候,它绝不直接碰原文件,而是先把内容一股脑写进一个临时文件。等所有数据都成功落盘了,再删掉原文件,把临时文件重命名顶上去。
这套路数能有效挡住写入过程中程序崩溃、或者突然断电导致原文件损坏的风险。Java、Redis这些系统,用的也是类似的思路。先把新版本在边上备好,确认无误再一键换防,稳。
二、QFile核心API------文件如何打开、读取与写入
QFile是Qt文件操作的核心角色。打开、读、写、关闭,一整套流程它全包了。绝对路径、相对路径都能用,跟Qt其他类配合起来也特别顺手。
2.1 QFile构造函数
QFile的构造函数不复杂,最常用的就一个:
cpp
QFile(const QString &name)
参数name是文件路径,绝对路径也好,相对路径也罢,都能传。绝对路径长这样:C:/test.txt;相对路径则是相对于程序当前运行目录来算的。
这里有个坑得拎出来敲一下:构造函数只是把QFile对象造出来,并不会真的去打开文件。 你想读写,必须老老实实调open()方法。光建对象不开门,文件永远在门外站着。
2.2 open():打开文件的基本方式
打开文件,靠的就是open()方法。原型长这样:
cpp
virtual bool open(QIODevice::OpenMode mode) override
返回值是bool:true表示打开成功,false表示失败。失败的原因五花八门------文件不存在、权限不够、路径写错了,都可能让你吃闭门羹。
参数mode是文件的打开模式,类型是QIODevice::OpenMode枚举的组合。想同时指定多个模式?用按位或 | 把它们串起来就行。
常用的打开模式,列在下面:
| 模式常量 | 数值 | 说明 |
|---|---|---|
| QIODevice::NotOpen | 0x0000 | 未打开 |
| QIODevice::ReadOnly | 0x0001 | 只读模式,文件必须存在 |
| QIODevice::WriteOnly | 0x0002 | 只写模式,文件不存在就创建;已存在则清空原有内容(除非同时指定 Append 模式) |
| QIODevice::ReadWrite | 0x0003 | 读写模式,相当于ReadOnly | WriteOnly |
| QIODevice::Append | 0x0004 | 追加模式,所有写入都落在文件末尾 |
| QIODevice::Truncate | 0x0008 | 截断模式,打开时清空原有内容 |
| QIODevice::Text | 0x0010 | 文本模式,自动转换换行符:Windows下把\n转成\r\n,Linux下保持\n 不变 |
| QIODevice::Unbuffered | 0x0020 | 无缓冲模式,绕过系统缓冲区,直接读写磁盘 |
| QIODevice::NewOnly | 0x0040 | 仅新建模式,只有文件不存在时才创建并打开;已存在则打开失败 |
| QIODevice::ExistingOnly | 0x0080 | 仅打开已存在文件模式,文件不存在就打开失败 |
看个例子,以只读文本模式打开文件:
cpp
QFile file("test.txt");
bool ret = file.open(QIODevice::ReadOnly | QIODevice::Text);
if (!ret) {
qDebug() << "打开文件失败";
return;
}
2.3 从QFile中读取文件内容
QFile 读文件的路子不止一条,不同场景,用不同招。
2.3.1 read():读取指定数量的数据
cpp
qint64 read(char *data, qint64 maxSize);
QByteArray read(qint64 maxSize);
两个重载,各管一摊。
- 第一个版本,把最多maxSize个字节的数据,塞进data指向的缓冲区里,返回实际读到的字节数。用这个版本,缓冲区得自己提前备好,读多少、读到哪,心里得有数。
- 第二个版本就省事多了:把最多maxSize个字节直接读进一个QByteArray对象并返回。不用手动管理缓冲区,QByteArray自己会打理,拿来就能用。日常读写,大多数人更偏爱这一版。
2.3.2 readLine():按行读取文件
cpp
qint64 readLine(char *data, qint64 maxSize);
QByteArray readLine(qint64 maxSize = 0);
按行读,一次一行,读到换行符\n或者凑满maxSize个字节就收手。
这招最适合文本文件,配置文件、日志文件,这些按行组织的内容,用readLine一行一行往外捞,逻辑天然对齐,处理起来也顺手。
2.3.3 readAll():一次读取全部内容
cpp
QByteArray readAll();
一口气把文件全部内容读完,返回一个QByteArray。这是最简单粗暴的读法,小文件用它,一行代码搞定,省心。
但有个前提得记牢:大文件别用readAll。 它会把整个文件一股脑加载进内存,文件稍微大点,内存占用就蹭蹭往上涨,轻则卡顿,重则直接把程序撑爆。大文件还是老老实实分块读,别贪这一时的方便。
一个重要提示:QByteArray转QString,比你想的还方便。
QByteArray可以非常顺滑地变成QString。QString本身就提供了直接接受QByteArray的构造函数和赋值运算符:
cpp
QByteArray byteArray = file.readAll();
QString text = byteArray; // 直接转换
// 或者更简洁的写法
QString text = file.readAll();
读出来就是字符串,转换这一步几乎不用你操心。
不过注意,这种转换默认走的是UTF-8编码。 要是文件用的是别的编码,比如GBK,那就得请出QTextCodec来做转换了。编码不对,读出来的就是一堆乱码,别怪文件,怪编码没对上。
2.4 写入文件:保存数据到磁盘
写文件用write(),一共三个重载版本:
cpp
qint64 write(const char *data, qint64 maxSize);
qint64 write(const char *data);
qint64 write(const QByteArray &byteArray);
返回值是实际写入的字节数,写失败了就返回-1。
参数data是要写进去的内容,C风格字符串也行,QByteArray对象也行,来者不拒。
但有个关键点必须拎出来说:QString不能直接当write()的参数。 你想写字符串,得先把它转成QByteArray。推荐用toUtf8(),把QString转成UTF-8编码的QByteArray,这是跨平台兼容性最好的一种方式:
cpp
QString text = "Hello World";
file.write(text.toUtf8());
当然,别的编码也能用,比如toLatin1()或者toLocal8Bit()。但用之前得掂量掂量,这些编码跨平台时容易出幺蛾子,换个系统、换个环境,乱码就找上门了。没特殊需求的话,toUtf8()是最稳的选择,别给自己找麻烦。
2.5 close():文件使用完成后的关闭操作
文件操作做完,最后一件事千万别忘,关文件。调用close()方法:
cpp
virtual void close();
关文件这个动作,本质上是把操作系统为这个文件分配的文件描述符表项给释放掉。每个进程的文件描述符表都是有固定上限的。Linux默认通常是1024。你光开不关,文件描述符就一个接一个地漏,漏到最后,表满了,后面再想打开任何文件,门都没有。这就是典型的文件描述符泄漏。
有人可能想:程序退出的时候,操作系统不是会自动把所有打开的文件都关掉吗?那还费劲手动关干嘛?
话是没错,但别指望操作系统给你兜底。养成手动关文件的好习惯,在长时间运行的服务程序里尤其重要。 一个跑几个月都不带重启的服务,文件开开关关成千上万次,你每漏一个,就离"打开失败"近一步。到时候排查起来,哭都来不及。用完就关,这是规矩。
三、文件读写实战:实现一个简易记事本
光讲API不过瘾,我们直接上手做一个完整的项目,一个简易记事本,支持打开和保存文本文件。把前面学的QFile那套东西,全都串起来用一遍。
3.1 从创建工程到搭建界面
先创建一个Qt Widgets Application项目,基类选QMainWindow。主窗口的骨架,就靠它撑起来。
3.1.1 mainwindow.h:声明窗口功能
cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPlainTextEdit>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// 处理"打开"菜单项的槽函数
void handleAction1();
// 处理"保存"菜单项的槽函数
void handleAction2();
private:
Ui::MainWindow *ui;
// 文本编辑框,作为成员变量方便在槽函数中访问
QPlainTextEdit* edit;
};
#endif // MAINWINDOW_H
这个头文件里,几件事交代得清清楚楚。
MainWindow继承自QMainWindow,这是主窗口程序的标准起手式。两个槽函数handleAction1和handleAction2,分别对应"打开"和"保存"两个菜单项的动作。名字起得随意了点,后面完全可以改成onOpen、onSave之类更见名知意的叫法,但功能上先跑通再说。
QPlainTextEdit* edit被拎出来当成员变量,不是随手一放。它得在槽函数里被访问,如果只放在构造函数里当局部变量,出了那个作用域就找不着了。提到成员变量的位置上,两个槽函数随时都能伸手拿到它,这是Qt里处理控件跨函数访问的常规操作。
3.1.2 mainwindow.cpp:实现窗口初始化
界面搭建的活儿,全在构造函数里完成。菜单栏、菜单项、文本编辑框,一股脑儿安排到位,再把信号槽接上,记事本的骨架就立起来了。
cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QFont>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 给窗口起个名字
this->setWindowTitle("简单的记事本");
// 拿到主窗口的菜单栏
QMenuBar* menuBar = this->menuBar();
// 加一个"文件"菜单
QMenu* menu = new QMenu("文件");
menuBar->addMenu(menu);
// 往菜单里塞"打开"和"保存"两个动作
QAction* action1 = new QAction("打开");
QAction* action2 = new QAction("保存");
menu->addAction(action1);
menu->addAction(action2);
// 创建文本编辑框,作为窗口的中心部件
edit = new QPlainTextEdit();
// 把字体调大一点,20 像素,看着舒服
QFont font;
font.setPixelSize(20);
edit->setFont(font);
this->setCentralWidget(edit);
// 信号槽接上:点菜单项,就触发对应的处理函数
connect(action1, &QAction::triggered, this, &MainWindow::handleAction1);
connect(action2, &QAction::triggered, this, &MainWindow::handleAction2);
}
MainWindow::~MainWindow()
{
delete ui;
}
这段代码里,几个动作环环相扣。
先设窗口标题,再拿菜单栏,this->menuBar()会帮你把菜单栏准备好,不用自己new。接着建一个"文件"菜单挂上去,再往里塞"打开"和"保存"两个QAction。到这里,菜单部分就齐了。
然后轮到文本编辑框。QPlainTextEdit被new出来,赋给成员变量edit,再把字体调到20像素,看着不费眼。最后用setCentralWidget把它安到窗口正中央,成了整个记事本的主战场。
收尾一步,connect把两个QAction的triggered信号,分别接到handleAction1和handleAction2上。菜单一点,槽函数就动,界面和逻辑就此接上了电。
3.2 打开文件:将内容读取到编辑器
轮到handleAction1()登场,把"打开文件"这条路走通:
cpp
#include <QFileDialog>
#include <QStatusBar>
#include <QFile>
void MainWindow::handleAction1()
{
// 1. 弹出"打开文件"对话框,让用户挑一个文件
QString path = QFileDialog::getOpenFileName(this);
// 2. 把选中的文件路径亮在状态栏上
QStatusBar* statusBar = this->statusBar();
statusBar->showMessage(path);
// 3. 拿路径构造 QFile,以只读方式打开
QFile file(path);
bool ret = file.open(QIODevice::ReadOnly);
if (!ret) {
// 打不开?状态栏报个信,然后收工
statusBar->showMessage(path + " 打开失败!");
return;
}
// 4. 一口气把文件内容全读出来
QString text = file.readAll();
// 5. 读完就关,别占着文件描述符
file.close();
// 6. 把内容塞进文本编辑框,用户立马就能看到
edit->setPlainText(text);
}
3.3 保存文件:将编辑内容写回磁盘
cpp
void MainWindow::handleAction2()
{
// 1. 弹出"保存文件"对话框,让用户定路径
QString path = QFileDialog::getSaveFileName(this);
// 2. 路径亮到状态栏上
QStatusBar* statusBar = this->statusBar();
statusBar->showMessage(path);
// 3. 拿路径构造 QFile,以只写方式打开
QFile file(path);
bool ret = file.open(QIODevice::WriteOnly);
if (!ret) {
// 打不开就报信,别硬撑
statusBar->showMessage(path + " 打开失败!");
return;
}
// 4. 取出编辑框里的内容,转成 UTF-8 写进文件
const QString& text = edit->toPlainText();
file.write(text.toUtf8());
// 5. 写完关闭文件
file.close();
}
跟打开那套几乎是一个模子:弹对话框、显示路径、开文件、写内容、关文件。唯一多出来的动作,是第4步里那个toUtf8(),QString不能直接往write()里塞,必须先转成QByteArray。UTF-8编码在这儿是跨平台最稳的选择,写出来的文件到哪儿都不会乱码。
3.4 简易记事本运行效果
编译跑起来,一个带"文件"菜单的窗口就立起来了,菜单里躺着"打开"和"保存"两项。
点"打开",文件选择对话框弹出来。选一个文本文件,内容立刻铺满编辑框,状态栏同时把文件的完整路径报出来。
点"保存",保存对话框登场。定好路径和文件名,编辑框里的内容就哗哗写进文件。
一个能读能写、有菜单有反馈的简易记事本,到这儿就算完工了。麻雀虽小,QFile的打开、读取、写入、关闭,全套流程都跑了一遍,算是一次完整的实战。
四、QFileInfo:获取文件详细信息
QFileInfo是Qt专门用来获取文件和目录属性信息的类。文件名、文件大小、路径、类型、创建时间、修改时间、访问时间......你能想到的属性,它基本都能捞出来。在C++17引入std::filesystem之前,想拿这些信息,非得跟操作系统原生API打交道,平台一换,代码就得重写。QFileInfo给了一套跨平台的统一接口,一处编写,到处能跑。
4.1 QFileInfo构造函数
最常用的两个构造函数:
cpp
QFileInfo(const QString &file);
QFileInfo(const QFile &file);
参数file可以是文件路径,也可以是一个现成的QFile对象。给路径就按路径查,给对象就按对象查,两条路都通。
4.2 QFileInfo常用API
QFileInfo手里的方法多得很,下面挑最常用的那些,一个个过一遍。
文件名相关:
- QString fileName() const:返回文件名,带后缀,比如test.txt。
- QString baseName() const:返回基本名,不带后缀,比如test。
- QString completeBaseName() const:返回完整的基本名。碰上test.tar.gz,它给的是 test.tar。
- QString suffix() const:返回后缀名。test.tar.gz丢进去,吐出来的是gz。
- QString completeSuffix() const:返回完整后缀名。test.tar.gz到这儿,出来的就是tar.gz。
后缀这一组特别值得玩味:suffix只认最后一个点后面的东西,completeSuffix则把从第一个点开始往后的一串全算上。差之毫厘,谬以千里,用哪个得看你要什么。
路径相关:
- QString path() const:返回文件所在路径,不含文件名。
- QString absolutePath() const:返回文件的绝对路径,把话说到最明白。
属性相关:
- qint64 size() const:返回文件大小,单位是字节。
- bool isFile() const:判断是不是普通文件。
- bool isDir() const:判断是不是目录。
- bool isExecutable() const:判断是不是可执行文件。
- bool exists() const:判断文件存不存在。
时间相关:
QDateTime fileTime(QFile::FileTime time) const:返回文件的时间信息。参数time有三种选择,QFile::FileBirthTime(创建时间)、QFile::FileModificationTime(修改时间)、QFile::FileAccessTime(访问时间)。想查哪个时间段,传对应的枚举就行。
4.3 通过代码获取文件信息
光说不练假把式,下面用一个简单例子,把QFileInfo拉出来遛遛。
4.3.1 widget.h:声明文件信息相关功能
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
头文件很常规,就一个按钮的槽函数on_pushButton_clicked,等着被点击触发。
4.3.2 widget.cpp:实现文件信息获取
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QDebug>
#include <QFileInfo>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// 弹出文件对话框,让用户挑一个文件
QString path = QFileDialog::getOpenFileName(this);
// 用选中的路径构造 QFileInfo 对象
QFileInfo fileInfo(path);
// 把文件的各种属性,一股脑打印出来
qDebug() << "文件名:" << fileInfo.fileName();
qDebug() << "文件后缀:" << fileInfo.suffix();
qDebug() << "文件路径:" << fileInfo.path();
qDebug() << "文件大小:" << fileInfo.size() << "字节";
qDebug() << "是否为普通文件:" << fileInfo.isFile();
qDebug() << "是否为目录:" << fileInfo.isDir();
qDebug() << "是否存在:" << fileInfo.exists();
}
逻辑很直白:点一下按钮,弹个文件选择框,选中之后,用路径造一个QFileInfo。接下来就是"体检报告"时间,文件名、后缀、路径、大小、是不是普通文件、是不是目录、存不存在,挨个查一遍,全部打到控制台上。一个对象,七项属性,文件的底细摸得清清楚楚。
4.3.3 运行效果
编译跑起来,点按钮,选一个文件,控制台就会吐出一串信息。比如选中ui_widget.h,输出大致是这样:
cpp
文件名: "ui_widget.h"
文件后缀: "h"
文件路径: "C:/Users/18106/Desktop/QT/qt/code/build-QFileInfo-Desktop_Qt_5_14_2_MinGW_64_bit-Debug"
文件大小: 1526 字节
是否为普通文件: true
是否为目录: false
是否存在: true
一条条看下来:文件名带后缀,后缀单独摘出来,路径不含文件名,大小精确到字节,类型判断、存在判断,全是明明白白。QFileInfo就这么点事,一个对象,把文件的里里外外都给你翻出来了。
五、Qt文件操作小结
这篇从理论到实战,把Qt文件操作的核心知识完整走了一遍。下面这些,是必须牢牢握在手里的东西。
Qt文件系统的核心优势,就三个字:跨平台。 它跟QString、QByteArray这些Qt核心类无缝集成,功能完善,错误处理也友好。比起C标准库和系统原生API,用起来省心得多。
I/O设备的继承体系,得心里有数。 所有I/O设备都从QIODevice派生,QFile又从QFileDevice继承而来。搞清楚这层关系,Qt I/O系统那套统一设计的思想,你才算摸到了门。
QFile的核心操作流程,一条都不能漏:
- 构造函数只负责创建对象,不打开文件。想读写,必须老老实实调open()。
- open()必须指定正确的打开模式,返回值也要认真处理,别打开了没打开都不知道。
- 读文件:小文件直接readAll(),文本文件按行读就用readLine()。
- 写文件:QString不能直接往里塞,必须先转成QByteArray。优先用toUtf8(),跨平台最稳。
- close()必须手动调。不关,文件描述符就一个接一个地漏,漏到最后,什么都打不开。
特殊场景,有特殊工具:
- 临时文件用QTemporaryFile,对象一销毁,文件自动删除,省心。
- 重要文件写入用QSaveFile,先写临时文件再原子替换,原文件损坏的风险被它挡在门外。
**文件信息获取,交给QFileInfo。**文件名、大小、路径、时间、类型,跨平台统一接口,不用再跟操作系统原生API纠缠。
实战部分,我们做了一个简易记事本。 QFileDialog选文件,QStatusBar报状态,QPlainTextEdit展示内容,打开和保存的完整逻辑一条龙跑通。这套组合拳打下来,文件操作的基本盘就稳了。
这些东西,是Qt桌面开发的地基。几乎每一个Qt应用,都绕不开文件操作。把这些吃透,你才能写出功能完善、稳定可靠的Qt程序。
写到这里,Qt文件操作这一篇就收尾了。
回头看看,其实核心就那几件事:QFile负责打开、读写、关闭,QFileInfo负责查户口,QSaveFile和QTemporaryFile在特殊场景兜底,再加上QFileDialog和QStatusBar一配合,一个像模像样的记事本就能跑起来。
文件操作这个东西,平时不起眼,可但凡是个正经桌面应用,就绕不开它。配置要存、日志要写、用户数据要持久化,程序得靠它记住点东西,不然每次重启都像失忆。把这一块吃透,你写的程序才算真正有了"记忆"。
动手把那个简易记事本敲一遍,再试着自己加点功能,比如另存为、最近打开、自动保存,感受一下文件操作从"能用"到"好用"的变化。后面遇到更复杂的场景,心里就有底了。
如果这篇文章对你有帮助,别忘了点个赞、点个收藏、点个关注。你的每一次反馈,都是我继续硬核输出的最大动力。我们下篇见。