Qt学习(十三)——文件读写文件系统结构图QFile读

文件的读写应用场景十分广泛,也是我们必须要学习的。以下是Qt中,文件系统的结构图:

文件系统结构图

在这里插入图片描述
文件的读写在网络编程中很常用,必须要拿下QAQ

QFile读文件

读取文件的步骤:获取文件路径→创建文件并指定路径→打开文件→读取文本→用户自定义操作→关闭文件。

//Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
protected:
    void Widget::openFile();
public:
    QGridLayout *layout=new QGridLayout(this);
    QPushButton *b1=new QPushButton(this);
    QPushButton *b2=new QPushButton(this);
    QTextEdit *editor=new QTextEdit(this);
};
#endif // WIDGET_H

复制代码
//Widget.cpp
#include "Widget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QFile>
#include <QFileDialog>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    b1->setText("读取文本");
    b2->setText("写入文本");
    layout->addWidget(b1,0,0,1,1);
    layout->addWidget(b2,0,1,1,1);
    layout->addWidget(editor,1,0,1,2);
    this->setLayout(layout);
    this->resize(300,400);
    connect(b1,&QPushButton::clicked,this,&Widget::openFile);
}

Widget::~Widget()
{

}
void Widget::openFile(){
    //生成一个文件对话框并获取文件路径
    QString path=QFileDialog::getOpenFileName(this,"open","C:/Users/MSI-NB/Desktop","text(*.txt)");
    if(!path.isEmpty()){    //若文件路径不为空
        //创建QFile对象并指定路径
        QFile file(path);
        //一定要记得打开文件,并设置读写模式
        file.open(QIODevice::ReadOnly);
        //读取文件内容,一次性全部读入并保存在字节数组对象array中,默认只识别UTF-8编码的文件,其余编码格式会产生乱码
        QByteArray array=file.readAll();
        //将读取到的文本内容显示在文本编辑框中
        this->editor->setText(array);
        //记得关闭文件
        file.close();
    }
}

复制代码

实现效果:
在这里插入图片描述
如果不想一次性读入所有文本,而是想要逐行读取,可以这样来实现:

#include "Widget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QFile>
#include <QFileDialog>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    b1->setText("读取文本");
    b2->setText("写入文本");
    layout->addWidget(b1,0,0,1,1);
    layout->addWidget(b2,0,1,1,1);
    layout->addWidget(editor,1,0,1,2);
    this->setLayout(layout);
    this->resize(300,400);
    connect(b1,&QPushButton::clicked,this,&Widget::openFile);
}

Widget::~Widget()
{

}
void Widget::openFile(){
    //生成一个文件对话框并获取文件路径
    QString path=QFileDialog::getOpenFileName(this,"open","C:/Users/MSI-NB/Desktop","text(*.txt)");
    if(!path.isEmpty()){    //若文件路径不为空
        //创建QFile对象并指定路径
        QFile file(path);
        //一定要记得打开文件,并设置读写模式
        file.open(QIODevice::ReadOnly);
        //读取文件内容,一次性全部读入并保存在字节数组对象array中,默认只识别UTF-8编码的文件,其余编码格式会产生乱码
        QByteArray array;
        //若文件未达到结尾
        while(!file.atEnd()){
            //逐行读取并拼接到字节数组array中
            array+=file.readLine();
        }
        //将读取到的文本内容显示在文本编辑框中
        this->editor->setText(array);
        //记得关闭文件
        file.close();
    }
}

复制代码

实现效果是一模一样的:
在这里插入图片描述
(只是在趁机安利Jay的乱舞春秋(◔v◔)

QFile写文件

回想在我们的日常操作中,写文件的步骤也是先对文本进行编辑,接着打开一个文件对话框,再选择要保存的路径,其实与读文件的实现方法差不多:

#ifndef WIDGET_H
#define WIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
protected:
    void Widget::openFile();
    void Widget::saveFile();
public:
    QGridLayout *layout=new QGridLayout(this);
    QPushButton *b1=new QPushButton(this);
    QPushButton *b2=new QPushButton(this);
    QTextEdit *editor=new QTextEdit(this);
};
#endif // WIDGET_H

复制代码
//Widget.cpp
#include "Widget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QFile>
#include <QFileDialog>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    b1->setText("读取文本");
    b2->setText("写入文本");
    layout->addWidget(b1,0,0,1,1);
    layout->addWidget(b2,0,1,1,1);
    layout->addWidget(editor,1,0,1,2);
    this->setLayout(layout);
    this->resize(300,400);
    connect(b1,&QPushButton::clicked,this,&Widget::openFile);
    connect(b2,&QPushButton::clicked,this,&Widget::saveFile);
}

Widget::~Widget()
{

}
void Widget::openFile(){
    //生成一个文件对话框并获取文件路径
    QString path=QFileDialog::getOpenFileName(this,"open","C:/Users/MSI-NB/Desktop","text(*.txt)");
    if(!path.isEmpty()){    //若文件路径不为空
        //创建QFile对象并指定路径
        QFile file(path);
        //一定要记得打开文件,并设置读写模式
        file.open(QIODevice::ReadOnly);
        //读取文件内容,一次性全部读入并保存在字节数组对象array中,默认只识别UTF-8编码的文件,其余编码格式会产生乱码

        QByteArray array=file.readAll();
        //将读取到的文本内容显示在文本编辑框中
        this->editor->setText(array);
        //记得关闭文件
        file.close();
    }
}
void Widget::saveFile(){
    //获取文本编辑框的内容并转化为QString对象
    QString text=this->editor->toPlainText();
    //打开一个文件对话框并获取保存文件的路径
    QString path=QFileDialog::getSaveFileName(this,"save","C:/Users/MSI-NB/Desktop","text(*.txt)");
    //创建一个文件对象,并关联路径
    QFile file;
    file.setFileName(path);
    //打开文件
    file.open(QIODevice::WriteOnly);
    //往文件中写入文本
    file.write(text.toUtf8());
    //记得关闭文件
    file.close();
}

复制代码

实现效果:
在这里插入图片描述
首先,我打开原来的歌词文件,在文本编辑区域修改了一句歌词,接着点击“写入文本”按钮,指定文件保存的路径(这里我没有新建一个文件,而是直接覆盖了原来的文件),然后再点击“读取文本”,读取出刚刚保存的文件,发现歌词已经被成功修改了。

说明:
在读取文本框内容的时候,我们调用了toPlainText()方法将文本转化为QString类型的字符串,但是要调用write()方法往文件中写入时,需要以QByteArray字节数组的形式,因此还需要调用toUtf8()方法将QString类型的字符串进行转化。

在这里补充几个Qt中常用的不同类型的文本流之间转化的方法:

QString → QByteArray:

QByteArray array=str.toUft8();	//str是QString对象
复制代码

QString对象不仅可以转化为UTF-8编码的字节数组,还可以转化为其它方式编码的字节数组,比如使用本地平台编码方式:

QByteString array=str.toLocal8Bit();
复制代码

QString → std::string(C++标准字符串) → char *:

qDebug()<<"typing "<<str.toStdString().data();	//text是QString对象
复制代码

QByteArray → char *:

char *b=array.data();	//array是QByteArray对象
复制代码

char * → QString:

char *p="abc";
QString str=QString(p);
复制代码
#include "Widget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QFile>
#include <QFileDialog>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    b1->setText("读取文本");
    b2->setText("写入文本");
    layout->addWidget(b1,0,0,1,1);
    layout->addWidget(b2,0,1,1,1);
    layout->addWidget(editor,1,0,1,2);
    this->setLayout(layout);
    this->resize(300,400);
    connect(b1,&QPushButton::clicked,this,&Widget::openFile);
    connect(b2,&QPushButton::clicked,this,&Widget::saveFile);
}

Widget::~Widget()
{

}
void Widget::openFile(){
    //生成一个文件对话框并获取文件路径
    QString path=QFileDialog::getOpenFileName(this,"open","C:/Users/MSI-NB/Desktop","text(*.txt)");
    if(!path.isEmpty()){    //若文件路径不为空
        //创建QFile对象并指定路径
        QFile file(path);
        //一定要记得打开文件,并设置读写模式
        file.open(QIODevice::ReadOnly);
        //读取文件内容,一次性全部读入并保存在字节数组对象array中,默认只识别UTF-8编码的文件,其余编码格式会产生乱码
        QByteArray array=file.readAll();
        //将读取到的文本内容显示在文本编辑框中
        this->editor->setText(array);
        //记得关闭文件
        file.close();
    }
}
void Widget::saveFile(){
    //获取文本编辑框的内容并转化为QString对象
    QString text=this->editor->toPlainText();
    //打开一个文件对话框并获取保存文件的路径
    QString path=QFileDialog::getSaveFileName(this,"save","C:/Users/MSI-NB/Desktop","text(*.txt)");
    //创建一个文件对象,并关联路径
    QFile file;
    file.setFileName(path);
    //打开文件
    file.open(QIODevice::WriteOnly);
    file.write(text.toUtf8());
    
    QByteArray qba=text.toUtf8();	//先将QString转化为QByteArray,采用UTF-8编码
    char *b=qba.data();	//再将QByteArray转化为char *
    
    qDebug()<<"typing "<<qba;	//打印QByteArray
    qDebug()<<"typing "<<b;	//打印char *
    //关闭文件
    file.close();
}
复制代码

实现效果:
在这里插入图片描述
输入英文,调用toUtf8()后得到的QByteArray对象与调用toUtf8().data()后得到的char * 变量的区别在于,前者多了双引号。
在这里插入图片描述
输入中文,调用toUtf8()后得到的QByteArray对象与调用toUtf8().data()后得到的char * 变量的区别除了前者多了双引号之外,输出char * 变量可以得到完整的中文字符串将字符串,而输出QByteArray对象得到的是一些UTF-8表示形式。

注意:如果需要输出中文,应该先用toUtf8()方法转化成Unicode编码,再用data()方法转化成我们可以识别的中文字符串。

QFileInfo获取文件信息

文件自身携带一些信息,如文件大小、创建时间、后缀名等等,要获取这些信息,需要用到QFileInfo。使用QFileInfo需要先引入<QFileInfo>头文件。

让我们在刚才读写文件的例子中,获取一下文件的信息:

#include "Widget.h"
#include <QGridLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QFile>
#include <QFileDialog>
#include <QDebug>
#include <QFileInfo>
#include <QDateTime>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    b1->setText("读取文本");
    b2->setText("写入文本");
    layout->addWidget(b1,0,0,1,1);
    layout->addWidget(b2,0,1,1,1);
    layout->addWidget(editor,1,0,1,2);
    this->setLayout(layout);
    this->resize(300,400);
    connect(b1,&QPushButton::clicked,this,&Widget::openFile);
    connect(b2,&QPushButton::clicked,this,&Widget::saveFile);
}

Widget::~Widget()
{

}
void Widget::openFile(){
    //生成一个文件对话框并获取文件路径
    QString path=QFileDialog::getOpenFileName(this,"open","C:/Users/MSI-NB/Desktop","text(*.txt)");
    if(!path.isEmpty()){    //若文件路径不为空
        //创建QFile对象并指定路径
        QFile file(path);
        //一定要记得打开文件,并设置读写模式
        file.open(QIODevice::ReadOnly);
        //读取文件内容,一次性全部读入并保存在字节数组对象array中,默认只识别UTF-8编码的文件,其余编码格式会产生乱码

        QByteArray array=file.readAll();
        //将读取到的文本内容显示在文本编辑框中
        this->editor->setText(array);

        //获取文件信息
        QFileInfo info(path);
        qDebug()<<"文件名:"<<info.fileName();
        qDebug()<<"文件大小:"<<info.size();
        qDebug()<<"文件类型:"<<info.suffix();
        qDebug()<<"文件创建时间:"<<info.created().toString("yyyy-MM-dd hh:mm:ss");

        //记得关闭文件
        file.close();
    }
}
复制代码

实现效果:
在这里插入图片描述
说明:文件大小以字节(Byte)为单位。

created()方法返回一个QDateTime对象,要想以文本形式输出时间,还需要调用toString()方法,并且指定输出的格式。调用toString()方法需要先引入<QDateTime>头文件,下面是帮助文档中对于格式的详细描述:
在这里插入图片描述

QDataStream读写文件

如果我们想以二进制流的形式对文件进行读写操作,就需要用到QDataStream。使用QDataStream,不仅可以操作文本文件,还可以操作音频、视频等类型的文件。

//Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#pragma execution_character_set("utf-8")
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
    void Widget::writeStream();
    void Widget::readStream();
};

#endif // WIDGET_H
复制代码
#include "Widget.h"
#include <QFile>
#include <QDataStream>
#include <QDebug>
//#define cout qDebug()<<"["<<__FILE__<<":"<<__LINE__<<"]"
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    writeStream();
    readStream();
}

Widget::~Widget()
{

}
void Widget::writeStream(){
    //创建一个文档
    QFile file("‪C:/Users/MSI-NB/Desktop/datastream.txt");
    //打开文件
    file.open(QIODevice::WriteOnly);
    //创建一个数据流对象,并与文档关联
    QDataStream stream(&file);
    //往数据流中写数据,因为数据流与文档关联,所以相当于往文档写数据
    stream<<QString("黑色柳丁")<<3155530;
    //关闭文件
    file.close();
}

复制代码

实现效果:
在这里插入图片描述
这个文件就算能直接打开也没有什么意义,因为它的内容是二进制流,所以我们打开看到的是一些乱码,需要通过QDataStream把它读出来,并转换成我们想要的格式。

#include "Widget.h"
#include <QFile>
#include <QDataStream>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    writeStream();
    readStream();
}

Widget::~Widget()
{

}
void Widget::writeStream(){
    //创建一个文档
    QFile file("‪C:/Users/MSI-NB/Desktop/datastream.txt");
    //打开文件
    file.open(QIODevice::WriteOnly);
    //创建一个数据流对象,并与文档关联
    QDataStream stream(&file);
    //往数据流中写数据,因为数据流与文档关联,所以相当于往文档写数据
    stream<<QString("黑色柳丁")<<3155530;
    //关闭文件
    file.close();
}
void Widget::readStream(){
    //创建一个文档
    QFile file("‪C:/Users/MSI-NB/Desktop/datastream.txt");
    //打开文档
    file.open(QIODevice::ReadOnly);
    //创建一个数据流对象,并与文档关联
    QDataStream stream(&file);
    //从数据流中读数据,因为数据流与文档关联,所以相当于从文档读数据
    //这里需要定义一些变量来接收输出的内容,会根据类型自动匹配
    //写入的顺序与读出的顺序是一致的
    QString str;
    int num;
    stream>>str>>num;
    //打印变量值检验结果是否正确
    qDebug()<<str.toUtf8().data()<<num;
}

复制代码

注意:从QDataStream对象中输出的数据会根据类型自动匹配,且读取和写入的顺序是一样的,先写入的会先被读取。

实现效果:
在这里插入图片描述
Tips:
调试的过程中,经常使用到qDebug(),但是还要打一对括号很麻烦,我们可以定义一个宏,用c++中的输出cout来替代。另外,利用c中的__FILE____LINE__,可以指示当前语句所在的源文件的文件名在文件中的位置(行数)。(__FILE__和__LINE__都是大小写敏感)

#include "Widget.h"
#include <QFile>
#include <QDataStream>
#include <QDebug>
#define cout qDebug()<<"["<<__FILE__<<":"<<__LINE__<<"]"	//定义一个便于输出和定位的宏
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    writeStream();
    readStream();
}

Widget::~Widget()
{

}
void Widget::writeStream(){
    //创建一个文档
    QFile file("‪C:/Users/MSI-NB/Desktop/datastream.txt");
    //打开文件
    file.open(QIODevice::WriteOnly);
    //创建一个数据流对象,并与文档关联
    QDataStream stream(&file);
    //往数据流中写数据,因为数据流与文档关联,所以相当于往文档写数据
    stream<<QString("黑色柳丁")<<3155530;
    //关闭文件
    file.close();
}
void Widget::readStream(){
    //创建一个文档
    QFile file("‪C:/Users/MSI-NB/Desktop/datastream.txt");
    //打开文档
    file.open(QIODevice::ReadOnly);
    //创建一个数据流对象,并与文档关联
    QDataStream stream(&file);
    //从数据流中读数据,因为数据流与文档关联,所以相当于从文档读数据
    //这里定义一些变量来接收输出的内容
    //写入的顺序与读出的顺序是一致的
    QString str;
    int num;
    stream>>str>>num;
    //打印变量值检验结果是否正确
    qDebug()<<str.toUtf8().data()<<num;
    cout<<str<<num;
}
复制代码

实现效果:
在这里插入图片描述

QTextStream操作文件

要想通过文本的方式操作文件,并为其指定读写的编码,就需要用到QTextStream。QTextStream的用法与QDataStream基本一致。主要的区别是QTextStream对象可以调用setCodec()方法来指定编码方式,参数如下:

在这里插入图片描述
但是用这个方式读文件会出现我们不想要的结果,因为它将所有内容都当成是字符串,这样一来,原先跟在QString字符串后面的int型数据也被识别成字符串,所以最好还是用readAll()或者readLine()来读取文件。

QBuffer

QBuffer用来操作内存文件。它的用法与QFile差不多,也需要先创建内存文件对象,打开内存文件,写入或读取内存文件,最后关闭内存文件。调用write()方法往内存文件中写入的内存其实是写在缓冲区的,要从缓冲区里把数据取出来,需要调用QBuffer对象的buffer()方法。另外,放到缓冲区的内容会根据写入的顺序依次排列,取出时也是取出缓冲区中所有的内容。

#include "Widget.h"
#include <QBuffer>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    writeFile();
}

Widget::~Widget()
{

}
void Widget::writeFile(){
	//创建内存文件
    QBuffer memFile;
    //打开内存文件
    memFile.open(QIODevice::WriteOnly);
    //向缓冲区写入数据
    memFile.write("hello");
    memFile.write("hi");
    //关闭文件
    memFile.close();
    //从缓冲区取出数据
    qDebug()<<memFile.buffer();

}
复制代码

实现效果:
在这里插入图片描述
在创建QBuffer对象时,可以在构造函数的参数列表中传入一个QByteArray对象,为这个QBuffer对象指定一个缓冲区。那么,在对QBuffer对象调用write()进行写入时,就会将数据写入到QByteArray对象中。

#include "Widget.h"
#include <QBuffer>
#include <QDebug>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    writeFile();
}

Widget::~Widget()
{

}
void Widget::writeFile(){
	//创建一个字节数组对象,此时为空
    QByteArray array;
    //创建一个内存文件并指定array为缓冲区
    QBuffer memFile(&array);
    //打开内存文件
    memFile.open(QIODevice::WriteOnly);
    //向缓冲区写入数据
    memFile.write("i am writing ");
    memFile.write("in buffer");
    //关闭文件
    memFile.close();
    //打印array的值
    qDebug()<<"array is "<<array;

}

复制代码

实现效果:
在这里插入图片描述
P.S:如有错误,欢迎指正~