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()<<"释放资源成功";
}

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

满山的猴子我的腚最红

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值