Qt 文件操作详解:从基础类到读写实战

📑 目录

  • [🚀 1. 引言](#🚀 1. 引言)
  • [📁 2. 输入输出设备类](#📁 2. 输入输出设备类)
  • [✏️ 3. 文件读写类](#✏️ 3. 文件读写类)
  • [📖 4. 示例1:读取文件内容](#📖 4. 示例1:读取文件内容)
  • [📝 5. 示例2:写文件](#📝 5. 示例2:写文件)
  • [📂 6. 文件和目录信息类](#📂 6. 文件和目录信息类)
  • [☁️ 7. 总结](#☁️ 7. 总结)
  • [🌲 8. 经典面试题](#🌲 8. 经典面试题)

🚀 1. 引言

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 提供了很多关于文件的类,通过这些类能够对文件系统进行操作,如文件读写、文件信息获取、文件复制或重命名等。本文将系统介绍 Qt 中文件操作相关的核心类、继承关系以及实际应用示例。

📁 2. 输入输出设备类

在 Qt 中,文件读写的类为 QFile。QFile 的父类为 QFileDevice,QFileDevice 提供了文件交互操作的底层功能。QFileDevice 的父类是 QIODevice,QIODevice 的父类为 QObject。

QIODevice 是 Qt 中所有输入输出设备(input/output device,简称 I/O 设备)的基础类。I/O 设备就是能进行数据输入和输出的设备,例如文件是一种 I/O 设备,网络通信中的 socket 是 I/O 设备,串口、蓝牙等通信接口也是 I/O 设备,所以它们也是从 QIODevice 继承来的。

Qt 中主要的一些 I/O 设备类的继承关系如下图所示:

上图中各类的说明如下:

  • QFile :用于文件操作和文件数据读写的类,使用 QFile 可以读写任意格式的文件。
  • QSaveFile :用于安全保存文件的类。使用 QSaveFile 保存文件时,它会先把数据写入一个临时文件,成功提交后才将数据写入最终的文件。如果保存过程中出现错误,临时文件里的数据不会被写入最终文件,这样就能确保最终文件中不会丢失数据或被写入部分数据。在保存比较大的文件或复杂格式的文件时可以使用这个类,例如从网络上下载文件等。
  • QTemporaryFile :用于创建临时文件的类。使用函数 QTemporaryFile::open() 就能创建一个文件名唯一的临时文件,在 QTemporaryFile 对象被删除时,临时文件被自动删除。
  • QTcpSocket 和 QUdpSocket:分别实现了 TCP 和 UDP 的类。
  • QSerialPort:实现了串口通信的类,通过这个类可以实现计算机与串口设备的通信。串口是比较古老的通信方式,一般在嵌入式系统上面开发板,通过串口和开发板之间进行交互。
  • QBluetoothSocket :用于蓝牙通信的类。手机和平板计算机等移动设备有蓝牙通信模块,笔记本电脑一般也有蓝牙通信模块。通过 QBluetoothSocket 类,就可以编写蓝牙通信程序,如编程实现笔记本电脑与手机的蓝牙通信。
  • QProcess :用于启动外部程序,并且可以给程序传递参数。相当于对 fork() 和 exec 操作进行封装。
  • QBuffer :以一个 QByteArray 对象作为数据缓冲区,将 QByteArray 对象当作一个 I/O 设备来读写。

✏️ 3. 文件读写类

在 Qt 中,文件的读写主要是通过 QFile 类来实现。在 QFile 类中提供了一些用来读写文件的方法。对于文件的操作主要有:

  • 读数据 :QFile 类中提供了多个方法用于读取文件内容,如 read()、readAll()、readLine() 等。
  • 写数据 :QFile 类中提供了多个方法用于往文件中写内容,如 write()、writeData() 等。
  • 关闭文件 :文件使用结束后必须用函数 close() 关闭文件。

访问一个设备之前,需要使用 open() 函数打开该设备,而且必须指定正确的打开模式。QIODevice 中所有的打开模式由 QIODevice::OpenMode 枚举变量定义,其取值如下:

打开模式 说明
QIODevice::NotOpen 没有打开设备
QIODevice::ReadOnly 以只读方式打开设备
QIODevice::WriteOnly 以只写方式打开设备
QIODevice::ReadWrite 以读写方式打开设备
QIODevice::Append 以追加方式打开设备,数据将写到文件末尾
QIODevice::Truncate 每次打开文件后重写文件内容,原内容将被删除
QIODevice::Text 在读文件时,行尾终止符会被转换为 '\n';当写入文件时,行尾终止符会被转换为本地编码。如 Win32 上为 '\r\n'
QIODevice::Unbuffered 无缓冲形式打开文件,绕过设备中的任何缓冲区
QIODevice::NewOnly 文件存在则打开失败,不存在则创建文件

📖 4. 示例1:读取文件内容

  1. 新建 Qt 项目,在 UI 文件中拖入一个 LineEdit,一个 pushButton,一个 TextEdit。当点击按钮时,弹出窗口选择要读取的文件,并将读取到的内容在 TextEdit 中显示;

  2. 在 "widget.cpp" 文件中实现对应功能;

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QFileDialog>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(ui->pushButton,&QPushButton::clicked,[=](){
        QString path = QFileDialog::getOpenFileName(this,//对话框的父类
                                                    "打开文件", //对话框的标题
                                                    "D:\\Qt\\tmp" //默认打开文件路径
                                                    );
        //将打开的文件路径放到lineEdit中
        ui->lineEdit->setText(path);
        //path 代表的是打开的文件的路径
        QFile file(path);
        file.open(QIODevice::ReadOnly);//表示以只读的方式打开文件
        QString str = file.readAll();//读取文件中的所有内容,一次读全部
        //将读取到的文件内容放到textEdit中
        ui->textEdit->setText(str);
        //记得用完要关闭文件!
        file.close();
    });
}

Widget::~Widget()
{
    delete ui;
}

实现效果如下:

📝 5. 示例2:写文件

在上述示例的基础上修改 "widget.cpp" 文件;

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QFileDialog>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(ui->pushButton,&QPushButton::clicked,[=](){
        QString path = QFileDialog::getOpenFileName(this,//对话框的父类
                                                    "打开文件", //对话框的标题
                                                    "D:\\Qt\\tmp" //默认打开文件路径
                                                    );
        //将打开的文件路径放到lineEdit中
        ui->lineEdit->setText(path);
        //path 代表的是打开的文件的路径
        QFile file(path);
        file.open(QIODevice::Append);//表示以追加的方式打开文件
        //写入文件
        file.write("【📃这是一个使用Qt写文件的案例】");
        file.close();
    });
}

Widget::~Widget()
{
    delete ui;
}

实现效果如下:

📂 6. 文件和目录信息类

QFileInfo 是 Qt 提供的一个用于获取文件和目录信息的类,如获取文件名、文件大小、文件修改日期等。QFileInfo 类中提供了很多的方法,常用的有:

  • isDir():检查该文件是否是目录;
  • isExecutable():检查该文件是否是可执行文件;
  • fileName():获得文件名;
  • completeBaseName():获取完整的文件名;
  • suffix():获取文件后缀名;
  • completeSuffix():获取完整的文件后缀;
  • size():获取文件大小;
  • isFile():判断是否为文件;
  • fileTime():获取文件创建时间、修改时间、最近访问时间等;

示例:在 "widget.cpp" 文件中添加如下代码:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
#include<QDateTime>
#include<QPushButton>
#include<QFileDialog>
#include<QFileInfo>
#include<QFileDevice>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(ui->btn,&QPushButton::clicked,[=](){
        QString path = QFileDialog::getOpenFileName(this,
                                                    "打开文件",
                                                    "D:\\Qt\\tmp");
        //创建一个获取文件信息的QFileInfo对象
        QFileInfo fileInfo(path);
        //1.获取文件名
        qDebug()<<"文件名:"<< fileInfo.fileName().toUtf8().data();
        //2.文件后缀名
        qDebug()<<"当前文件的后缀名是:"<<fileInfo.suffix().toUtf8().data();
        //3.文件大小
        qDebug()<<"文件大小是:"<<fileInfo.size();
        //5.文件路径
        qDebug()<<"文件路径:"<<fileInfo.path().toUtf8().data();
        //6.是否是文件
        qDebug()<<"是否是文件:"<<fileInfo.isFile();
        //7.是否是目录
        qDebug()<<"是否是目录:"<<fileInfo.isDir();
        //8.文件创建时间
        QDateTime creaate_time = fileInfo.fileTime(QFileDevice::FileBirthTime);
        qDebug()<<"创建时间为:"<<creaate_time.toString("yyyy-MM-dd hh:mm::ss").toUtf8().data();
        //9.文件的修改时间
        QDateTime modify_time = fileInfo.lastModified();
        qDebug()<<"文件最后的修改时间:"<<modify_time.toString("yyyy-MM-dd hh:mm:ss");
    });
}
Widget::~Widget()
{
    delete ui;
}

实现效果如下:

☁️7. 总结

本文详细介绍了 Qt 中文件操作的核心知识

,包括 QIODevice 及其子类的继承体系、QFile 的读写方法、打开模式枚举,以及 QFileInfo 获取文件信息的方法。通过读取文件、写入文件和获取文件信息三个实战示例,相信读者已经掌握了 Qt 文件操作的基本流程。在实际开发中,根据需求选择合适的类和方法,就能高效地完成文件相关的功能。

🌲8. 经典面试题

面试题1:QFile 与 QSaveFile 有什么区别?分别在什么场景下使用?

参考答案:

QFile 是 Qt 中最基础的文件读写类,直接对文件进行读写操作,适用于大多数普通文件读写场景。而 QSaveFile 是用于安全保存文件的类,它先把数据写入一个临时文件,成功提交后才将数据写入最终文件。如果保存过程中出现错误,临时文件里的数据不会被写入最终文件,从而确保最终文件不会丢失数据或被写入部分数据。

适用场景: 当需要写入大量数据,或写入过程中一旦出错会导致原文件损坏时,应优先使用 QSaveFile。例如从网络上下载文件、保存配置文件等场景。而普通的小文件读写,使用 QFile 即可。

面试题2:QIODevice::OpenMode 中 Append 和 Truncate 有什么区别?

参考答案:

  • QIODevice::Append:以追加方式打开设备,数据将写到文件末尾,原有内容保留。
  • QIODevice::Truncate:每次打开文件后重写文件内容,原内容将被删除。

注意: 两者不能同时使用。如果以 Append 方式打开,写入的数据会追加到文件末尾;如果以 Truncate 方式打开,文件原有内容会被清空,再写入新数据。

面试题3:QFile 读取文件时,read()、readAll()、readLine() 有什么区别?

参考答案:

  • read(qint64 maxSize):从文件中读取最多 maxSize 字节的数据,适合分块读取大文件,避免一次性加载过多内容导致内存占用过高。
  • readAll():一次性读取文件中的所有内容,返回一个 QByteArray。适合读取小文件,简单方便。
  • readLine():读取一行数据,以换行符为分隔。适合逐行处理文本文件,如日志文件、配置文件等。

实际建议: 对于大文件,应使用 read() 分块读取;对于小文件,可以直接使用 readAll();对于文本文件需要逐行处理时,使用 readLine() 更高效。

面试题4:QFileInfo 中 fileName()、completeBaseName()、suffix()、completeSuffix() 有什么区别?

参考答案:

假设文件路径为 C:\Users\Lenovo\Desktop\example.tar.gz:

  • fileName():返回文件名,包含所有后缀,结果为 example.tar.gz。
  • completeBaseName():返回完整的文件名(不含后缀),结果为 example.tar。
  • suffix():返回最后一个后缀名,结果为 gz。
  • completeSuffix():返回完整的后缀名,结果为 tar.gz。

记忆技巧: 带 complete 前缀的方法返回更完整的信息,completeBaseName() 返回去掉最后一个后缀的文件名,completeSuffix() 返回所有后缀。

面试题5:使用 QFile 读写文件时,为什么一定要调用 close()?如果不调用会怎样?

参考答案:

close() 用于关闭文件,释放文件句柄。如果不调用 close(),文件句柄会一直占用,可能导致以下问题:

  1. 资源泄漏:文件句柄是系统资源,不释放会导致资源耗尽,尤其是在循环中反复打开文件时。
  2. 数据丢失:写入的数据可能还停留在缓冲区中,没有真正写入磁盘,关闭文件时才会刷新缓冲区。
  3. 文件被占用:文件一直被占用,其他程序无法访问或删除该文件。

注意: 在 Qt 中,QFile 对象被销毁时,析构函数会自动调用 close(),但为了代码清晰和及时释放资源,建议在文件使用完毕后显式调用 close()。

面试题6:QTemporaryFile 有什么特点?它的临时文件什么时候会被删除?

参考答案:

QTemporaryFile 是用于创建临时文件的类。使用 QTemporaryFile::open() 就能创建一个文件名唯一的临时文件,避免文件名冲突。它的特点是:

  • 文件名唯一,不会与其他文件冲突。
  • 当 QTemporaryFile 对象被销毁时,临时文件会被自动删除,无需手动清理。

适用场景: 程序运行过程中需要临时存储数据,且不需要长期保留的场景,例如缓存数据、中间结果等。

相关推荐
Wang's Blog1 小时前
Java 项目实战: 外卖平台优化-YApi接口管理平台与文档导入导出
java·开发语言·yapi
jimy11 小时前
基类没有虚析构函数,基类指针删除派生类对象,行为是未定义的
开发语言·c++
迅猛龙办公室1 小时前
python获取星期字符串
开发语言·python
泡海椒2 小时前
JQuick-Excel dateFormat 转换实战:日期值与 FORMAT 显示格式的边界
开发语言·python·excel
白杨尚青2 小时前
C++入门篇(十三):vector(上)——动态数组:构造、空间增长与迭代器失效
开发语言·c++·笔记·算法·stl
江江爱编程2 小时前
java零基础保姆级教学01
java·开发语言
沐晓时光3 小时前
C语言入门,深入理解指针(2)
c语言·开发语言
江华森3 小时前
华为云CCE/Kubernetes云原生技术体系 — 总学习笔记
面试
2601_962885723 小时前
如何用 Python 做均线粘合选股?(多头发散前的变盘候选)
开发语言·python