【QT】系统事件入门 -- 文件 QFile基础和示例

一、Qt 文件概述

🔥 文件操作是应用程序必不可少的部分。Qt 作为⼀个通用开发库,提供了跨平台的文件操作能力。Qt 提供了很多关于⽂件的类,通过这些类能够对文件系统进行操作,如文件读写、文件信息获取、文件制或重命名等

二、输入输出设备类

在 Qt 中,文件读写的类为 QFile

  • QFile 的父类为 QFileDeviceQFileDevice 提供了文件交互操作的底层功能
  • QFileDevice 的父类是 QIODeviceQIODevice 的父类为 QObject
  • QIODevice 是 Qt 中所有输入输出设备(input/output device,简称 I/O 设备)的基础类,I/O 设备就是能进行数据输入和输出的设备
  • 例如文件是⼀种 I/O 设备,网络通信中的 socket 是 I/O 设备,串口、蓝牙等通信接口也是 I/O 设备,所以它们也是从 QIODevice 继承来的。

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

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

三、文件读写类

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

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

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

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

代码示例

代码示例1:创建和打开文件

c 复制代码
#include <QFile>
#include <QTextStream>
#include <QDebug>

void createAndWriteFile(const QString &filePath) {
    QFile file(filePath);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QTextStream out(&file);
        out << "Hello, Qt!\n";
        file.close();
    } else {
        qDebug() << "无法打开文件:" << file.errorString();
    }
}

在上述示例中,QFile 对象被创建,并尝试以写模式打开文件。如果成功,则使用 QTextStream 向文件写入文本。

代码示例2:读取文件

c 复制代码
void readFile(const QString &filePath) {
    QFile file(filePath);
    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QTextStream in(&file);
        while (!in.atEnd()) {
            QString line = in.readLine();
            qDebug() << line;
        }
        file.close();
    } else {
        qDebug() << "无法打开文件:" << file.errorString();
    }
}

综合示例代码如下:

c 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPlainTextEdit>
#include <QFileDialog>

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* ac1 = new QAction("打开"); // 添加菜单项
    QAction* ac2 = new QAction("保存");
    menu->addAction(ac1);
    menu->addAction(ac2);

    // 指定一个输入框 edit 为全局变量
    edit = new QPlainTextEdit();
    QFont font;
    font.setPixelSize(25);
    edit->setFont(font);
    this->setCentralWidget(edit);

    // 连接 QAction 信号槽
    connect(ac1, &QAction::triggered, this, &MainWindow::handleAction1);
    connect(ac2, &QAction::triggered, this, &MainWindow::handleAction2);
}

void MainWindow::handleAction1()
{
    // 1. 先弹出 "打开文件" 对话框,选择打开文件
    QString path = QFileDialog::getOpenFileName(this);
    // 2. 把文件名显示到状态栏里
    QStatusBar* statusBar = this->statusBar();
    statusBar->showMessage(path);
    // 3. 根据用户选择路径,构造一个 QFile 对象,并且打开文件
    QFile file(path);
    if(!file.open(QIODevice::ReadOnly)){
        // 打开文件失败
        statusBar->showMessage(path + " 打开失败");
        return;
    }
    // 4. 读取文件
    // 这里需要确保打开的文件是文本文件才行
    // 如果是 二进制文件, 交给 QString,
    QString text = file.readAll();
    // 5. 关闭文件,并设置读取内容
    file.close();
    edit->setPlainText(text);
}

void MainWindow::handleAction2()
{
    // 1. 先弹出 "打开文件" 对话框,选择打开文件
    QString path = QFileDialog::getOpenFileName(this);
    // 2. 显示文件名
    QStatusBar* statusBar = this->statusBar();
    statusBar->showMessage(path);
    // 3. 根据用户选择路径,构造一个 QFile 对象,并且打开文件
    QFile file(path);
    if(!file.open(QIODevice::WriteOnly)){
        statusBar->showMessage(path + " 打开失败");
        return;
    }
    // 4. 写文件
    const QString& text = edit->toPlainText();
    file.write(text.toUtf8());

    file.close();
}

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

结果如下:

四、文件和目录信息类

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

方法名称 作用
isDir() 检查该文件是否是目录
isExecutable() 检查该文件是否是可执行文件
fileName() 获得文件名
completeBaseName() 获取完整的文件名
suffix() 获取文件后缀名
completeSuffix() 获取完整文件后缀
size() 获取文件大小
isFile() 判断是否为文件
fileTime() 获取文件创建时间、修改时间、最近访问时间等
c 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QDebug>
#include <QFileDialog>

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

    QPushButton* button = new QPushButton("查看文件属性", this);
    connect(button, &QPushButton::clicked, [=](){
       QString path = QFileDialog::getOpenFileName(this);
       QFileInfo fileInfo(path); // 构造对象
       // 打印相关属性
       qDebug() << fileInfo.fileName();
       qDebug() << fileInfo.suffix();
       qDebug() << fileInfo.path();
       qDebug() << fileInfo.size();
       qDebug() << fileInfo.isFile();
       qDebug() << fileInfo.isDir();
       // ...
    });
}

五、其他

① 创建删除目录

c 复制代码
#include <QDir>

void createDirectory(const QString &dirPath) {
    QDir dir;
    if (!dir.exists(dirPath)) {
        if (dir.mkpath(dirPath)) {
            qDebug() << "目录创建成功:" << dirPath;
        } else {
            qDebug() << "无法创建目录:" << dir.errorString();
        }
    } else {
        qDebug() << "目录已存在:" << dirPath;
    }
}

void deleteDirectory(const QString &dirPath) {
    QDir dir(dirPath);
    if (dir.exists()) {
        if (dir.rmdir(dirPath)) {
            qDebug() << "目录删除成功:" << dirPath;
        } else {
            qDebug() << "无法删除目录:" << dir.errorString();
        }
    } else {
        qDebug() << "目录不存在:" << dirPath;
    }
}

② 选择文件

QFileDialog::getOpenFileName() 函数可以用来打开文件选择对话框,让用户选择文件。

c 复制代码
#include <QApplication>
#include <QFileDialog>
#include <QPushButton>
#include <QWidget>
#include <QVBoxLayout>
#include <QMessageBox>

class FileSelector : public QWidget {
public:
    FileSelector() {
        QPushButton *button = new QPushButton("选择文件", this);
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(button);
        
        // 点击按钮时打开文件选择器
        connect(button, &QPushButton::clicked, this, &FileSelector::selectFile);
    }

private slots:
    void selectFile() {
        QString fileName = QFileDialog::getOpenFileName(this, tr("打开文件"), "", tr("所有文件 (*.*);;文本文件 (*.txt)"));
        
        if (!fileName.isEmpty()) {
            QMessageBox::information(this, tr("选择的文件"), fileName);
        }
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    FileSelector window;
    window.setWindowTitle("文件选择示例");
    window.resize(300, 100);
    window.show();
    
    return app.exec();
}

说明:

QFileDialog::getOpenFileName():用于打开单个文件选择对话框。

参数解释:

  • 第一个参数是父窗口。
  • 第二个参数是对话框标题。
  • 第三个参数是默认路径。
  • 第四个参数是过滤器,用于指定可以选择的文件类型。

③ 选择文件夹

QFileDialog::getExistingDirectory() 用来让用户选择文件夹

c 复制代码
#include <QApplication>
#include <QFileDialog>
#include <QPushButton>
#include <QWidget>
#include <QVBoxLayout>
#include <QMessageBox>

class FolderSelector : public QWidget {
public:
    FolderSelector() {
        QPushButton *button = new QPushButton("选择文件夹", this);
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(button);
        
        // 点击按钮时打开文件夹选择器
        connect(button, &QPushButton::clicked, this, &FolderSelector::selectFolder);
    }

private slots:
    void selectFolder() {
        QString folderName = QFileDialog::getExistingDirectory(this, tr("选择文件夹"), "");

        if (!folderName.isEmpty()) {
            QMessageBox::information(this, tr("选择的文件夹"), folderName);
        }
    }
};

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    FolderSelector window;
    window.setWindowTitle("文件夹选择示例");
    window.resize(300, 100);
    window.show();
    
    return app.exec();
}
  • 注意:QFileDialog::getExistingDirectory():用于打开选择文件夹的对话框。

④ 选择多个文件

如果你想让用户一次选择多个文件,可以使用

c 复制代码
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("选择多个文件"), "", tr("所有文件 (*.*);;图片文件 (*.png *.jpg)"));

if (!fileNames.isEmpty()) {
    for (const QString &fileName : fileNames) {
        // 处理每个文件
        qDebug() << "选择的文件:" << fileName;
    }
}

上面三个的区别如下:

名称 作用
QFileDialog::getOpenFileName() 选择单个文件
QFileDialog::getExistingDirectory() 选择文件夹
QFileDialog::getOpenFileNames() 选择多个文件

这段代码允许用户同时选择多个文件,返回的是 QStringList,可以遍历进行处理。

⑤ 过滤文件类型

在文件选择对话框中,可以使用过滤器来限制用户选择某些特定类型的文件

c 复制代码
QString fileName = QFileDialog::getOpenFileName(this, tr("打开文件"), "", tr("图像文件 (*.png *.jpg);;文本文件 (*.txt);;所有文件 (*.*)"));
相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术13 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript