【QT】系统相关:QT文件

🎬 个人主页艾莉丝努力练剑
专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


文章目录

  • [前言 && 全文梗概](#前言 && 全文梗概)
  • [1 ~> QT文件系统概述](#1 ~> QT文件系统概述)
    • [1.1 I / O设备继承体系](#1.1 I / O设备继承体系)
    • [1.2 QFile相关子类简介](#1.2 QFile相关子类简介)
  • [2 ~> QFile核心API详解](#2 ~> QFile核心API详解)
    • [2.1 构造函数](#2.1 构造函数)
    • [2.2 打开文件open()](#2.2 打开文件open())
    • [2.3 读文件](#2.3 读文件)
      • [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 ~> 实战:简易记事本实现](#3 ~> 实战:简易记事本实现)
    • [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 ~> QFileInfo文件信息类](#4 ~> QFileInfo文件信息类)
    • [4.1 构造函数](#4.1 构造函数)
    • [4.2 常用API](#4.2 常用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 运行效果)
    • [5 ~> 总结](#5 ~> 总结)
  • 结尾



前言 && 全文梗概

导入语

文件操作是所有桌面应用程序不可或缺的核心功能,从简单的文本读写到复杂的配置文件管理、数据持久化都离不开它。Qt作为跨平台开发框架,最大的优势之一就是提供了统一的文件操作API,彻底解决了C/C++原生库和系统API(如Linux的open/read/write、Windows的CreateFile)的平台兼容性问题。Qt诞生于1991年,早于C++标准库的标准化进程,因此其文件系统封装经过了长期的迭代优化,与QStringQByteArray等Qt核心类无缝集成,使用起来比C++17才引入的std::filesystem更加便捷高效。

本文将系统讲解Qt文件操作的核心知识,包括I/O设备的继承体系、QFile类的完整API使用、从零实现一个支持打开和保存功能的简易记事本、以及QFileInfo类获取文件属性的方法。所有内容均配套完整的可运行代码,适合作为Qt入门学习的复习资料,帮助你快速回忆起文件操作的核心逻辑和常见坑点。

思维导图


1 ~> QT文件系统概述

Qt作为通用跨平台开发库,提供了完善的文件系统操作能力,涵盖文件读写、文件信息获取、文件复制/重命名、目录遍历等所有常见功能。

在Qt出现之前,文件操作主要依赖两种方式:一是C语言标准库的fopen/fclose/fread/fwrite系列函数,二是操作系统原生API。这两种方式都存在明显局限性:C标准库功能有限,无法获取文件的详细属性;系统API则完全不具备跨平台性,Linux下的代码无法直接在Windows上运行。

Qt诞生于1991年,早于C++标准的正式发布(1998年),因此其文件系统封装经过了30多年的迭代优化,不仅完美解决了跨平台问题,还与QStringQByteArray等Qt核心类实现了无缝集成。虽然C++17引入了std::filesystem库,但Qt的文件操作API在易用性、功能完整性和与Qt生态的兼容性上仍然具有明显优势,因此在Qt项目中强烈推荐使用Qt自身的文件操作类。

Qt文件操作的核心流程与原生方式完全一致,都遵循"打开→读/写→关闭"的基本逻辑,只是将这些操作封装成了更加易用的类和方法。

1.1 I / O设备继承体系

Qt中所有输入输出设备都继承自QIODevice类,它定义了所有I/O设备的通用接口,如打开、关闭、读、写等。文件、网络套接字、串口、蓝牙、进程通信、内存缓冲区等都属于I/O设备,因此它们都继承自QIODevice

完整的继承关系如下:

  • 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还提供了两个非常实用的QFile子类,解决了特定场景下的文件操作问题:

  • QTemporaryFile :用于创建临时文件。当QTemporaryFile对象销毁时,对应的临时文件会自动被删除,非常适合存储临时数据,避免手动清理临时文件的麻烦。
  • QSaveFile:用于安全地写入文件。它采用了"先写临时文件,写完再替换原文件"的原子性策略:写入数据时不会直接修改原文件,而是先将内容写入一个临时文件,当所有数据都成功写入后,再删除原文件并将临时文件重命名为原文件名。这种策略可以有效避免写入过程中程序崩溃或断电导致原文件损坏的问题,Java、Redis等众多系统都采用了类似的安全写策略。

2 ~> QFile核心API详解

QFile类是Qt文件操作的核心,它提供了完整的文件打开、读、写、关闭功能,支持绝对路径和相对路径,与Qt的其他类配合非常方便。

2.1 构造函数

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
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提供了多种读文件的方法,适用于不同的场景:

2.3.1 read()方法

cpp 复制代码
qint64 read(char *data, qint64 maxSize);
QByteArray read(qint64 maxSize);
  • 第一个版本将最多maxSize个字节的数据读取到data指向的缓冲区中,返回实际读取的字节数。
  • 第二个版本将最多maxSize个字节的数据读取到QByteArray对象中并返回,使用起来更加方便,不需要手动管理缓冲区。

2.3.2 readLine()方法

cpp 复制代码
qint64 readLine(char *data, qint64 maxSize);
QByteArray readLine(qint64 maxSize = 0);
  • 按行读取文件,每次读取一行数据,直到遇到换行符\n或达到maxSize字节。
  • 适合读取文本文件,特别是配置文件、日志文件等按行组织的内容。

2.3.3 readAll()方法

cpp 复制代码
QByteArray readAll();
  • 一次性读取文件的所有内容,返回QByteArray对象。
  • 这是最简单易用的读文件方法,适合读取小文件。对于大文件,不建议使用readAll(),因为它会将整个文件加载到内存中,可能导致内存占用过高。

重要提示QByteArray可以非常方便地转换为QStringQString提供了直接接受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()方法,但要注意跨平台兼容性问题。

2.5 关闭文件close()

文件操作完成后,必须调用close()方法关闭文件:

cpp 复制代码
virtual void close();

关闭文件的本质是释放操作系统为该文件分配的文件描述符表项。每个进程的文件描述符表都有固定的上限(Linux默认通常是1024),如果只打开文件而不关闭,会导致文件描述符泄漏,最终耗尽所有文件描述符,使得后续无法打开任何文件。

即使程序退出时操作系统会自动关闭所有打开的文件,但养成手动关闭文件的好习惯仍然非常重要,特别是在长时间运行的服务程序中。


3 ~> 实战:简易记事本实现

下面我们通过一个完整的实战项目,来巩固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

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

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 保存文件功能实现

实现handleAction2()槽函数,完成保存文件的功能:

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

3.4 运行效果

编译运行程序后,会出现一个带有"文件"菜单的窗口,包含"打开"和"保存"两个菜单项:

  • 点击"打开",会弹出文件选择对话框,选择一个文本文件后,文件内容会显示在文本编辑框中,状态栏会显示文件的完整路径。
  • 点击"保存",会弹出保存文件对话框,选择保存路径和文件名后,文本编辑框中的内容会被保存到指定的文件中。

4 ~> 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 常用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
  • 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

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

4.3.3 运行效果

编译运行程序后,点击按钮弹出文件选择对话框,选择一个文件后,控制台会输出该文件的各种属性信息,例如选择ui_widget.h文件,输出如下:

bash 复制代码
文件名: "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

5 ~> 总结

本文系统讲解了Qt文件操作的核心知识,涵盖了从基础理论到实战应用的完整流程,以下是需要重点掌握的内容:

  1. Qt文件系统的核心优势 :完美的跨平台兼容性,与QStringQByteArray等Qt核心类无缝集成,功能完善且错误处理友好,比C++标准库和系统API更加易用。
  2. I/O设备继承体系 :所有I/O设备都继承自QIODeviceQFile继承自QFileDevice,了解这一继承关系有助于理解Qt I/O系统的统一设计思想。
  3. QFile核心操作流程
    1. 构造函数仅创建对象,不打开文件,必须调用open()方法。
    2. open()方法必须指定正确的打开模式,并对返回值进行错误处理。
    3. 读文件:小文件推荐使用readAll(),文本文件按行读取使用readLine()
    4. 写文件:QString必须转换为QByteArray,优先使用toUtf8()保证跨平台兼容性。
    5. close()方法必须手动调用,避免文件描述符泄漏导致的资源耗尽问题。
  4. 特殊场景文件操作
    1. 临时文件使用QTemporaryFile,对象销毁自动删除文件。
    2. 重要文件写入使用QSaveFile,通过原子写策略避免原文件损坏。
  5. 文件信息获取 :使用QFileInfo可以跨平台获取文件的所有属性信息,无需调用操作系统原生API。
  6. 实战应用要点 :通过简易记事本项目,掌握了QFileDialog文件选择对话框、QStatusBar状态栏、QPlainTextEdit文本编辑框的使用,以及完整的文件打开和保存逻辑。

这些知识是Qt桌面开发的基础,几乎所有Qt应用程序都会用到文件操作。熟练掌握这些内容,能够帮助你快速开发出功能完善、稳定可靠的Qt应用程序。


结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!" "技术之路难免有困惑,但同行的人会让前进更有方向。" |

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾

【Qt】事件

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
凤山老林1 小时前
81-Java Scanner 类
java·开发语言
j_xxx404_1 小时前
MySQL数据库基础硬核解析:从 C/S 网络服务到磁盘文件与存储引擎
linux·运维·服务器·开发语言·数据库·mysql·ai
沐苏瑶1 小时前
深入浅出 Java 文件操作与 IO:从文件系统到数据流实战
java·开发语言
海鸥-w1 小时前
用python (fastapi)做项目第二天实现新闻列表和新闻详情接口
开发语言·python·fastapi
Cloud_Shy6181 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第四章 Item 25 - 26)
开发语言·人工智能·经验分享·笔记·python·学习方法
likerhood1 小时前
服务器下载 Hugging Face 模型笔记:以 Qwen2.5-Coder-7B-CL 为例
运维·服务器·笔记
caimouse2 小时前
Reactos 第 4 章 对象管理 — 4.8 系统调用 NtDuplicateObject / 4.9 系统调用 NtClose
开发语言·windows·架构
zh路西法9 小时前
【navigation2全局路径更新频率修正】行为树框架的巧妙利用
linux
xieliyu.10 小时前
Java算法精讲:双指针(二)
java·开发语言·算法