c++QT文件IO

1、QFileDialog文件对话框

与QMessageBox一样,QFileDialog也继承了QDialog类,直接使用静态成员函数弹窗。弹出的结果(选择文件的路径)通过返回值获取。

1)获取一个打开或保存的文件路径

// 获取一个打开或保存的文件路径
// 参数1:父对象(this)
// 参数2:标题(执行操作:打开/保存)
// 参数3:在哪个目录中打开,默认值表示项目的工作目录
// 参数4:文件过滤器(过滤文件类型)
QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";//文件过滤器
// 返回值:选择的文件路径,如果选择失败,返回空字符
QString QFileDialog:: getOpenFileName(
    QWidget * parent = 0,const QString & caption = QString(),const QString & dir = QString(), const QString & filter = QString())[static]QString QFileDialog:: getSaveFileName(
    QWidget * parent = 0, const QString & caption = QString(), const QString & dir = QString(), const QString & filter = QString())[static]

需要注意的是,QFileDialog只是一个窗口类,本身不具备任何IO的能力。

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{
    ui->setupUi(this);connect(ui->pushButtonOpen,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));connect(ui->pushButtonSave,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}
Dialog::~Dialog()
{delete ui;
}
void Dialog::btnClickedSlot()
{if(ui->pushButtonOpen == sender()){
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";
        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);if(path != ""){
            ui->textBrowserOpen->append(path);
            readPath = path;}else if(readPath == ""){QMessageBox::warning(this,"提示","请选择要打开的文件");}}else if(ui->pushButtonSave == sender()){
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";
        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);if(path != ""){
            ui->textBrowserSave->append(path);
            writePath = path;}else if(writePath == ""){QMessageBox::warning(this,"提示","请选择保存的文件");}}else if(ui->pushButtonCopy == sender()){}
}

2、QFileInfo 文件信息类

只需要创建出对象后,通过各种成员函数直接获取文件信息。

部分函数如下:

1)构造函数

// 构造函数
// 参数为文件路径,如果文件非法,仍然可以创建出QFileInfo对象。
QFileInfo:: QFileInfo(const QString & file)

2)判断文件是否存在

// 判断文件是否存在
// 如果存在返回true、如果不存在返回false
bool QFileInfo:: exists() const

3)返回文件大小,如果不存在返回0

// 返回文件大小,如果不存在返回0
qint64 QFileInfo:: size() const

4)返回基础文件名,不包含后缀

// 返回基础文件名,不包含后缀
QString QFileInfo:: baseName() const

5)获取文件最后修改日期

// 获取文件最后修改日期
QDateTime QFileInfo:: lastModified() const

6)获取文件可读性

// 获取文件可读性
// 返回值为ture可读否则不可读
bool QFileInfo:: isReadable() const

#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
    connect(ui->pushButtonSave,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}
Dialog::~Dialog()
{
    delete ui;
}
void Dialog::printFileInfo()
{
    //创建文件对象
    QFileInfo fileInfo(readPath);
    if(!fileInfo.exists())
    {
        QMessageBox::warning(this,"提示","无效文件");
        return;
    }
    //获取文件大小
    qint64 fileSize = fileInfo.size();
    QString text = QString::number(fileSize);//int类型转QString类型
    text.prepend("文件大小:").append("字节");//prepend()在text前面插入字符,append()在text后面添加字符
    ui->textBrowserOpen->append(text);
    //获取文件名称
    text = fileInfo.baseName();//前面的text已经打印,重新用只需要覆盖即可。
    text.prepend("文件名称:");
    ui->textBrowserOpen->append(text);
    //获取文件最后修改日期
    text = fileInfo.lastModified().toString("修改日期:yyyy-MM-dd:hh-mm-ss");//获取后直接转为QString类型
    ui->textBrowserOpen->append(text);
    //文件可读性
    bool result = fileInfo.isReadable();//返回值为bool类型
    if(result)
    {
        ui->textBrowserOpen->append("文件可读");
    }
    else
    {
        ui->textBrowserOpen->append("文件不可读");
    }
}
void Dialog::btnClickedSlot()
{
    if(ui->pushButtonOpen == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";
        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);
        if(path != "")
        {
            ui->textBrowserOpen->append(path);
            readPath = path;
            printFileInfo();
        }
        else if(readPath == "")
        {
            QMessageBox::warning(this,"提示","请选择要打开的文件");
        }
    }
    else if(ui->pushButtonSave == sender())
    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";
        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);
        if(path != "")
        {
            ui->textBrowserSave->append(path);
            writePath = path;
        }
        else if(readPath == "")
        {
            QMessageBox::warning(this,"提示","请选择要保存的文件");
        }
    }
    else if(ui->pushButtonCopy == sender())
    {    }
}

3、QFile文件读写类

在Qt中所有IO类都继承自QIODevice类,QIODevice类中规定了最基础的IO相关的接口。这些接口虽然在不同的派生类中可能实现有所不同,但是调用方式一致。

1)QFile文件读写类构造函数

// 构造函数
// 参数为文件路径,如果是非法路径,也能创建出QFile对象,但是不能正常IO输入输出操作。
QFile:: QFile(const QString & name)

2)判断QFile对应的文件是否存在

// 判断QFile对应的文件是否存在
bool QFile:: exists() const

3)打开数据流

// 打开数据流
// 参数为打开的模式,只读模式、只写模式、读写模式等等
// 返回值:打开的结果
bool QIODevice:: open(OpenMode mode)[virtual]

4)判断是否读到文件末尾

// 判断是否读到文件末尾
// 返回值,true读到文件末尾,反之未读到
bool QFileDevice:: atEnd() const[virtual]

5)读取最大长度为maxSize个的自己到返回值中

// 读取最大长度为maxSize个的自己到返回值中
QByteArray QIODevice:: read(qint64 maxSize)

6)构造一个空字节的字节数组

// 构造函数,QByteArray是Qt中常用的数组
// 构造一个空字节的数组
QByteArray:: QByteArray()

7)向文件流中写数据

// 写出数据
// 参数为写出的内容
// 返回值为实际的数据写出的字节数,出错返回值为-1
qint64 QIODevice:: write(const QByteArray & byteArray)

8)刷新缓冲区

// 刷新缓冲区
bool QFileDevice:: flush()

9)关闭数据流

// 关闭数据流
void QIODevice:: close() [virtual]

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    connect(ui->pushButtonOpen,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
    connect(ui->pushButtonSave,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
    connect(ui->pushButtonCopy,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}
Dialog::~Dialog()
{
    delete ui;
}
void Dialog::printFileInfo()
{
    //创建文件对象
    QFileInfo fileInfo(readPath);
    if(!fileInfo.exists())
    {
        QMessageBox::warning(this,"提示","无效文件");
        return;
    }
    //获取文件大小
    qint64 fileSize = fileInfo.size();
    QString text = QString::number(fileSize);//int类型转QString类型
    text.prepend("文件大小:").append("字节");//prepend()在text前面插入字符,append()在text后面添加字符
    ui->textBrowserOpen->append(text);
    //获取文件名称
    text = fileInfo.baseName();//前面的text已经打印,重新用只需要覆盖即可。
    text.prepend("文件名称:");
    ui->textBrowserOpen->append(text);
    //获取文件最后修改日期
    text = fileInfo.lastModified().toString("修改日期:yyyy-MM-dd:hh-mm-ss");//获取后直接转为QString类型
    ui->textBrowserOpen->append(text);
    //文件可读性
    bool result = fileInfo.isReadable();//返回值为bool类型
    if(result)
    {
        ui->textBrowserOpen->append("文件可读");
    }
    else
    {
        ui->textBrowserOpen->append("文件不可读");
    }
}void Dialog::copy()
{
    //判断是否有目标文件与路径
    if(readPath == "")
    {
        QMessageBox::warning(this,"提示","请选择要读取的文件");
        return;
    }
    if(writePath == "")
    {
        QMessageBox::warning(this,"提示","请选择要写入的文件路径");
        return;
    }
    //拷贝时屏蔽拷贝按钮
    ui->pushButtonCopy->setEnabled(false);//使能为false
    //创建QFile对象
    QFile readFile(readPath);
    QFile writeFile(writePath);
    //打开文件流
    readFile.open(QIODevice::ReadOnly);//只读模式打开
    writeFile.open(QIODevice::WriteOnly);//只写模式
    //添加进度条效果
    qint64 totalSize = readFile.size();//获取文件总大小
    qint64 hasResd = 0;//已经读写的大小
    QByteArray array;//字节数组类
    //判断是否读到结尾,读到结尾返回true
    while(!readFile.atEnd())
    {
        array = readFile.read(1024);//每次读取1KB
        qint64 writeRet = writeFile.write(array);//返回值为本次写出的大小
        if(writeRet == -1)//如果写出函数的返回值为-1表示写出失败
        {
            QMessageBox::critical(this,"错误","文件拷贝失败");
        }
        //将写出的数据设置给进度条
        hasResd += writeRet;//获取到已经写入总大小
        int per = hasResd*100/totalSize;//计算百分比
        ui->progressBar->setValue(per);//设置给进度条
    }
    //刷新缓冲区
    writeFile.flush();
    //关闭数据流
    readFile.close();
    writeFile.close();
    ui->pushButtonCopy->setEnabled(true);//拷贝完使能true
    //拷贝完成提示
    QMessageBox::information(this,"提示","拷贝完成");
}
void Dialog::btnClickedSlot()
{
    if(ui->pushButtonOpen == sender())
    {
        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";
        QString path = QFileDialog::getOpenFileName(this,"打开","D:/",filter);
        if(path != "")
        {
            ui->textBrowserOpen->append(path);
            readPath = path;
            printFileInfo();
        }
        else if(readPath == "")
        {
            QMessageBox::warning(this,"提示","请选择要打开的文件");
        }
    }
    else if(ui->pushButtonSave == sender())
    {        QString filter = "所有文件(*.*);;Qt(*.cpp *.h *.ui *.pro)";
        QString path = QFileDialog::getSaveFileName(this,"保存","D:/",filter);
        if(path != "")
        {
            ui->textBrowserSave->append(path);
            writePath = path;
        }
        else if(readPath == "")
        {
            QMessageBox::warning(this,"提示","请选择要保存的文件");
        }
    }
    else if(ui->pushButtonCopy == sender())
    {
        copy();
    }
}

使用:先选择原文件路径(打开文件,选择需要拷贝的源文件)。点击保存文件按钮,指定保存文件的路径,及名称(需要加后缀)。点击开始拷贝。

【思考】:上面的代码有问题吗?超大代码拷贝,占用资源,如果执行其他操作时造成指令阻塞。

4、UI与耗时操作

在默认情况下,Qt的项目是单线程的。这个自带的主线程主要用于处理程序的主要任务和UI交互,也被为UI线程。

如果在主线程中实现耗时操作(IO或复杂算法)会导致主线程原本执行的操作被阻塞,甚至无法关闭,形成“假死”的现象。

当操作系统发现某个进行无法正常关闭时,会弹出程序未响应窗口引导用户选择是否强制关闭当前进程。

解决方案是多线程。

5、QThread线程类

5.1 复现程序未响应

QThread类是Qt的线程类,可以使用sleep函数模拟耗时操作。

1)休眠msleep强制线程休眠msecs 个毫秒

// 强制线程休眠msecs 个毫秒 void QThread:: msleep(unsigned long msecs)[static]

#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    connect(ui->pushButton,SIGNAL(clicked()),
            this,SLOT(btnSleepClickedSlot()));    connect(ui->pushButton_2,SIGNAL(clicked()),
            this,SLOT(close()));
}Dialog::~Dialog()
{
    delete ui;
}void Dialog::btnSleepClickedSlot()
{
    qDebug() << "睡眠开始";
    QThread::msleep(15000);
    qDebug() << "睡眠结束";
}

5.2 创建并启动一个子线程

主线程以外的所有线程都是子线程。子线程不能执行主线程的UI操作,只能指向耗时操作。(不要用栈区创建线程)

创建并启动一个自定义的子线程步骤:

  1. 在Qt Creator中选中项目名称,鼠标右键,点击“添加新文件
  2. 在弹出的窗口中,先设置类名,然后再选择基类名称QObject,最后点击“下一步”:

3、在项目管理界面,直接点击完成,可以看到线程类的文件已经创建。

  1. 选择新建的头文件,把继承的QObject更改为QThread。

  1. 选择新建的.cpp文件,把透传构造的QObject更改为QThread。

  1. run函数,在自定义线程中,覆盖基类中QThread的run函数。

此函数是子线程执行的起始点,也是子线程的结束点

// 此函数是子线程执行的起始点,也是子线程的结束点
void QThread:: run() [virtual][protected]

7、创建自定义子线程对象,并调用start函数启动子线程

// 启动子线程,调用此函数后,会在子线程中执行run函数。
// 参数为子线程执行的优先级,默认值为创建时所在的线程相同优先级
void QThread:: start(Priority priority = InheritPriority)

mythread.h/.cpp

dialog.h/.cpp

5.3 异步刷新

在实际开发中,两个线程不可能毫无关系的前提下各干各的,最常见的情况是主线程分配一个耗时任务给子线程,子线程需要把耗时任务的执行的执行情况反馈给主线程。主线程刷新子线程耗时操作,并展示对应的UI效果。

【例子】:子线程执行文件拷贝,主线程显示拷贝的进度。

通常子线程是主线程对象的子对象,因此异步通信刷新就是对象通信问题,使用信号槽解决。

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{
    ui->setupUi(this);connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(btnSleepClickedSlot()));}Dialog::~Dialog()
{delete ui;
}void Dialog::btnSleepClickedSlot()
{
    ui->pushButton->setEnabled(false);// 创建子线程对象并启动
    MyThread *mt = new MyThread(this);connect(mt,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));
    mt->start();
}void Dialog::valueSlot(int value)
{
    ui->progressBar->setValue(value);if(value == 100){
        ui->pushButton->setEnabled(true);this->hide();   // 隐藏主窗口this->show();QMessageBox::information(this,"通知","拷贝完成");}
}

myThread.cpp

#include "mythread.h"MyThread::MyThread(QObject *parent) : QThread(parent)
{}MyThread::~MyThread()
{
}void MyThread::run()
{for(int i = 0;<= 100;i++){QThread::msleep(100);
        emit valueSignal(i);}
}

练习:线程对象构造函数传参

子线程执行文件拷贝,主线程显示拷贝的进度。注:真正的文件拷贝。

线程传参.h

线程传参.cpp构造函数

创建子线程对象传参

方法二:成员函数构造调用传参

成员函数构造

成员函数构造调用传参

5.4 线程停止

子线程往往执行耗时操作,耗时操作又往往伴随着循环,因此并不建议使用粗暴的方式直接停止线程,因为强行停止线程会导致耗时资源无法回收等问题。

可以通过给循环设置标志位的方式使线程停止。

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);      connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(btnSleepClickedSlot()));
}
Dialog::~Dialog()
{
    delete ui;
}
void Dialog::btnSleepClickedSlot()
{
    if(ui->pushButton->text() == "开始拷贝")
    {
    //创建子线程对象并启动
    mt = new myThread(this);
    connect(mt,SIGNAL(valueSignal(int)),this,SLOT(valueSlot(int)));
    mt->setRuningState(true);
    mt->start();
    ui->pushButton->setText("停止拷贝");
    }
    else if(ui->pushButton->text() == "停止拷贝")
    {
        ui->pushButton->setText("开始拷贝");
        mt->setRuningState(false);
    }
}
void Dialog::valueSlot(int value)
{
    ui->progressBar->setValue(value);
    if(value == 100)
    {
        ui->pushButton->setEnabled(true);
        this->hide();//隐藏主窗口,让出焦点
        this->show();
        QMessageBox::information(this,"通知","拷贝完成");
    }
}

mythread.cpp                                              

#include "mythread.h"
myThread::myThread(QObject *parent) : QThread(parent)
{
}
myThread::~myThread()
{
}
bool myThread::getRuningState() const
{
    return runingState;
}
void myThread::setRuningState(bool value)
{
    runingState = value;
}
void myThread::run()
{
    for(int i= 0;i<=100 && runingState;i++)
    {
        QThread::msleep(100);
        emit valueSignal(i);
    }
    qDebug()<<"释放资源成功";
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/416738.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

插入排序(一)——直接插入排序与希尔排序

目录 一.前言 二.排序的概念及其运用 1.1排序的概念 1.2 常用排序算法 三.常用排序算法的实现 3.1 插入排序 3.1.1 基本思想 3.1.2 直接插入排序 3.1.3 希尔排序&#xff08;缩小增量排序&#xff09; 四.全部代码 sort.c sort.h test.c 五.结语 一.前言 本文我们…

微软使其AI驱动的阅读导师免费

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【android】 android 里写jni

目录 &#xff08;1&#xff09; 环境准备 (2) 关联c文件到gradle文件 &#xff08;3&#xff09; 生成了 (4) 书写 &#xff08;5&#xff09; 使用 &#xff08;6&#xff09;业务调用 参考文档 &#xff08;1&#xff09; 环境准备 ndk, cmake (2) 关联c文件到gr…

学习笔记应用——创建用户账户并且拥有自己的信息

一、创建用户账户 将建立一个用户注册和身份验证系统&#xff0c;让用户能够注册账户&#xff0c;进而登录和注销。我们将创建一个新的应用程序&#xff0c;其中包含与处理用户账户相关的所有功能。 创建user 我们首先使用命令 startapp 来创建一个名为 users 的应用程序&…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--大模型、扩散模型、视觉导航

专属领域论文订阅 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 关注留下邮箱可每日定时收到论文更新服务 分类: 大语言模型LLM视觉模型VLM扩散模型视觉导航具身智能&#xff0c;机器人强化学习开放词汇&a…

springcloud OpenFeign服务接口调用

文章目录 代码下载地址OpenFeign简介OpenFeign使用步骤测试 OpenFeign超时控制超时设置&#xff0c;故意设置超时演示出错情况服务提供方8001故意写暂停程序服务消费方80添加超时方法PaymentFeignService服务消费方80添加超时方法OrderFeignController测试YML文件里需要开启Ope…

2.【C语言】(函数指针||sizeof||笔试题)

0x01.函数指针 void test(const char* str) {printf("%s\n", str); }int main() {void (*pf)(const char*) test;//pf是函数指针变量void (*pfarr[10])(const char*);//pfarr是存放函数指针的数组void (*(*p)[10])(const char*) &pfarr;//p是指向函数指针数组…

day3:基于UDP模型的简单文件下载

思维导图 tftp文件下载客户端实现 #include <head.h> #define SER_PORT 69 #define SER_IP "192.168.125.223" int link_file() {int sfdsocket(AF_INET,SOCK_DGRAM,0);if(sfd-1){perror("socket error");return -1;}return sfd; } int filedownloa…

【51单片机系列】单片机与PC进行串行通信

一、单片机与PC机串行通信的设计 工业现场的测控系统中&#xff0c;常使用单片机进行监测点的数据采集&#xff0c;然后单片机通过串口与PC通信&#xff0c;把采集的数据串行传送到PC机上&#xff0c;再在PC机上进行数据处理。 PC机配置的都是RS-232标准串口&#xff0c;为D型…

element-ui 打包流程源码解析(下)

目录 目录结构和使用1&#xff0c;npm 安装1.1&#xff0c;完整引入1.2&#xff0c;按需引入 2&#xff0c;CDN3&#xff0c;国际化 接上文&#xff1a;element-ui 打包流程源码解析&#xff08;上&#xff09; 文章中提到的【上文】都指它 ↑ 目录结构和使用 我们从使用方式来…

2.C语言——控制语句

控制语句 1.分支语句/判断语句if 语句if...else 语句if...else if...else语句 switch语句 2.循环语句 while 语句 do...while 语句 for 语句 3.转向语句 break continue go to 1.分支语句/判断语句 if 语句 if(boolean_expression) { /* 如果布尔表达式为真将执行的语句 */ } …

使用WAF防御网络上的隐蔽威胁之SQL注入攻击

SQL注入攻击是一种普遍存在且危害巨大的网络安全威胁&#xff0c;它允许攻击者通过执行恶意的SQL语句来操纵或破坏数据库。 这种攻击不仅能够读取敏感数据&#xff0c;还可能用于添加、修改或删除数据库中的记录。因此&#xff0c;了解SQL注入攻击的机制及其防御策略对于保护网…