Qt学习笔记(三十二):Qt 中的文件读写操作

一、文件系统:

文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库,提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象,这些设备具有读写字节块的能力。下面是 I/O 设备的类图(Qt5):

  • QIODevice:所有 I/O 设备类的父类,提供了字节块读写的通用操作以及基本接口;

  • QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现。

  • QFlie:访问本地文件或者嵌入资源;

  • QTemporaryFile:创建和访问本地文件系统的临时文件;

  • QBuffer:读写QbyteArray, 内存文件;

  • QProcess:运行外部程序,处理进程间通讯;

  • QAbstractSocket:所有套接字类的父类;

  • QTcpSocket:TCP协议网络数据传输;

  • QUdpSocket:传输 UDP 报文;

  • QSslSocket:使用 SSL/TLS 传输数据;

文件系统分类:

  • 顺序访问设备:

是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节,这其中,QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。

  • 随机访问设备:

可以访问任意位置任意次数,还可以使用 QIODevice::seek() 函数来重新定位文件访问位置指针,QFile、QTemporaryFile 和 QBuffer 是随机访问设备。

 

二、QFile 读写文件:

在 ui 界面上拖两个按钮和一个文本编辑控件,如下:

代码如下:

// 读文件
void Widget::on_btnReadFile_clicked()
{
    // 获取文件路径
    QString filePath = QFileDialog::getOpenFileName();
    if (!filePath.isEmpty())
    {
        // 根据文件路径实例化一个文件对象
        QFile file(filePath);
        
        // 以只读方式打开文件
        if (file.open(QFile::ReadOnly))
        {
            // 读取文件:一次读取文件中所有内容
//            QByteArray buf = file.readAll();
            
            // 读取文件:一行一行读取
            QByteArray buf;
            while (!file.atEnd())
            {
                buf += file.readLine();
            }
            
            // 注意:默认情况下,如果文件中含有中文时,只有 UTF8 格式的文件能正常读取,
            // 其他格式的文件读取出的中文数据都是乱码。
            
            // 显示到 textEdit 上
            ui->textEdit->setText(buf);
        }
        
        // 关闭文件
        file.close();
    }
}

// 写文件
void Widget::on_btnWriteFile_clicked()
{
    // 保存文件路径
    QString filePath = QFileDialog::getSaveFileName();
    if (!filePath.isEmpty())
    {
        // 根据文件路径实例化一个文件对象
        QFile file(filePath);
        
        // 以只写方式打开文件
        if (file.open(QFile::WriteOnly))
        {
            // 获取 textEdit 中内容;toPlainText 表示获取 textEdit 中内容的纯文本数据
            QString text = ui->textEdit->toPlainText();
            
            // 写入文件:并将文件格式设置为 utf8
//            file.write(text.toUtf8());
            
            // 写入文件:将数据转换成标准 C++ 的 char* 格式;
            // 生成的文件还是 utf8 格式,即默认写入的数据格式就是 utf8;
//            file.write(text.toStdString().data());
            
            // 写入文件:文件格式为本地平台8位编码(windows 下默认为 ANSI)
            file.write(text.toLocal8Bit());
        }
        
        // 关闭文件
        file.close();
    }
}

QFileDialog 的使用参考 Qt学习笔记(十二):标准文件对话框

 

三、QFileInfo 获取文件信息:

// 获取文件路径
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
    QFileInfo info(filePath);
    qDebug() << info.absoluteFilePath();    // 获取包含文件名的绝对路径
    qDebug() << info.absolutePath();        // 获取不包含文件名的绝对路径

    qDebug() << info.fileName();            // 获取文件名(不包含路径)
    qDebug() << info.filePath();            // 获取文件名(包括路径,绝对的或相对的)
    qDebug() << info.path();                // 获取文件路径(不包含文件名)

    // 获取文件的创建日期和事件("年-月-日 时:分:秒:毫秒")
    qDebug() << info.created().toString("yyyy-MM-dd HH:mm:ss:zzz");

    qDebug() << info.exists();              // 判断文件是否存在

    qDebug() << info.suffix();              // 获取文件后缀

    qDebug() << info.isExecutable();        // 是否是可执行文件
    qDebug() << info.isDir();               // 是否是目录
    qDebug() << info.isFile();              // 是否是文件
    qDebug() << info.isHidden();            // 是否是隐藏文件
}

 

四、QDataStream 读写文件:

QDataStream 类提供二进制数据的序列化,到一个 IO 设备。

数据流是编码信息的二进制流,它完全独立于主机的操作系统、CPU 或字节顺序。例如,通过 windows 下 PC 编写的数据流,可以被运行在 Solaris 上的 Sun SPARC 读取。

还可以使用数据流来读取/写入未经编码的原始二进制数据。如果想要“解析”输入流,请参阅 QTextStream。

QDataStream 类实现了 c++ 基本数据类型的序列化,如 char、short、int、char * 等。更复杂数据的序列化是通过将数据分解为基本单元来实现的。

QDataStream 可以用来操作图片、音频、视频等非文本数据。

写文件:

// 写文件
void Widget::on_btnWriteFile_clicked()
{
    QFile file("file.txt");
    
    // 以只写方式打开文件
    if (file.open(QIODevice::WriteOnly))
    {
        // 根据文件对象实例化一个数据流,向数据流里写数据,就是向文件里写数据
        QDataStream stream(&file);   
        
        // 使用输出字符写数据,可以写字符串,也可以整型,或者其他类型的数据;
        // 注意:因为写入的是二进制数据,所以即使写入的是 .txt 文件,文件中也是乱码;
        stream << QString("主要看气质") << 22;
        
        file.close(); // 关闭文件
    }
    
    qDebug() << "写文件成功";
}

读文件:

// 读文件
void Widget::on_btnReadFile_clicked()
{
    QFile file("file.txt");
    if (!file.exists())
    {
        qDebug() << "文件不存在";
    }
    else
    {
        // 以只读方式打开文件
        if (file.open(QFile::ReadOnly))
        {
            // 创建数据流对象
            QDataStream stream(&file);
            
            QString str;
            int a;
            
            // 读取数据。
            // 注意:读取数据的类型和顺序,需要和写入数据的类型和顺序 保持一致.
            stream >> str >> a;
            
            // Qt 默认写入的数据是 utf8 格式的
            qDebug() << str.toUtf8().data() << a;
            
            file.close(); // 关闭文件
        }
    }
}

 

使用 QDataStream 读取图片,并转换成 base64 字符串:

// 读文件
void Widget::on_btnReadFile_clicked()
{
    // 获取文件路径
    QString filePath = QFileDialog::getOpenFileName();
    if (!filePath.isEmpty())
    {
        // 创建文件对象
        QFile file(filePath);
        
        // 以只读方式打开文件
        if (file.open(QFile::ReadOnly))
        {
            // 创建数据流
            QDataStream stream(&file);
            
            char *buf = new char[1024];
            QByteArray array;
            
            // 循环读取数据
            while (!stream.atEnd())
            {
                // 读取数据到缓冲区 buf 中,最多读取 1024 个字节,返回实际读取的字节数;
                int len = stream.readRawData(buf, 1024);

                // 将读取的二进制数据转换成 字节数组
                array.append(QByteArray(buf, len));
            }
            delete buf;
            
            // 将读取的数据转换成 base64 字符串
            QString str = QString(array.toBase64());
            ui->textEdit->setText(str);
            
            // 不知道为什么此处的 qDebug() 方法不能输出!!!
            qDebug() << str;
            
            file.close(); // 关闭文件
        }
    }
}

读取结果:

使用 QDataStream 将 base64 字符串转换成图片:将上图 textEdit 中的数据写入图片;

// 写文件
void Widget::on_btnWriteFile_clicked()
{
    // 获取保存文件路径
    QString filePath = QFileDialog::getSaveFileName();
    if (!filePath.isEmpty())
    {
        // 创建文件对象
        QFile file(filePath);
        
        // 以只写方式打开文件
        if (file.open(QFile::WriteOnly))
        {
            // 获取 textEdit 上的文本信息
            QString text = ui->textEdit->toPlainText();
            
            // 将 base64 数据转换成 字节数组
            QByteArray array = QByteArray::fromBase64(text.toUtf8());
            
            // 创建数据流
            QDataStream stream(&file);
            
            // 写入数据:参数1表示缓存数据,参数2表示缓存数据的字节长度;返回实际写入的字节数。
            int len = stream.writeRawData(array.data(), array.length());
            qDebug() << QString::number(len);
            
            file.close(); // 关闭文件
        }
    }
}

 

上面是使用 QDataStream 读写图片,并和 base64 字符串的互相转换,比较麻烦;Qt 还有更简单的方法读写图片数据,并和 base64 字符串互相转换,如下所示:

读取图片数据,并转换成 base64 字符串:

// 读文件
void Widget::on_btnReadFile_clicked()
{
    // 获取文件路径
    QString filePath = QFileDialog::getOpenFileName();
    if (!filePath.isEmpty())
    {
        // 声明字节数组
        QByteArray array;
        // 声明缓冲区,指向字节数组(当向缓冲区写入数据时,就是在向字节数组中写入数据)
        QBuffer buf(&array);
        
        // 将图片数据保存到缓冲区
        QPixmap pixmap(filePath);
        pixmap.save(&buf, "jpg");
        
        // 将数据转换成 base64 字符串
        QString str = QString(array.toBase64());
        ui->textEdit->setText(str);
        qDebug() << str;
    }
}

将 base64 字符串数据保存为图片:

// 写文件
void Widget::on_btnWriteFile_clicked()
{
    // 获取保存文件路径
    QString filePath = QFileDialog::getSaveFileName();
    if (!filePath.isEmpty())
    {
        // 获取 textEdit 上的数据
        QString str = ui->textEdit->toPlainText();
        
        // 从给定字节数组加载一个图片对象
        QPixmap pixmap;
        pixmap.loadFromData(QByteArray::fromBase64(str.toLocal8Bit()));
        
        // 保存图片
        pixmap.save(filePath);
    }
}

 

五、QTextStream 操作文件:

QTextStream 类为读写文本提供了一个方便的接口。

QTextStream 可以在 QIODevice、QByteArray 或 QString 上操作。使用 QTextStream 的流操作符,可以方便地读写单词、行和数字。对于生成文本,QTextStream 支持字段填充和对齐的格式化选项,以及数字的格式化。

使用 QTextStream 读取控制台输入和写入控制台输出也是常见的。QTextStream 可识别区域设置,并将使用正确的编解码器自动解码标准输入。

写入文件:

// 写文件
void Widget::on_btnWriteFile_clicked()
{
    // 创建文件对象
    QFile file("file.txt");
    
    // 以只写方式打开文件
    if (file.open(QFile::WriteOnly))
    {
        // 创建文本流对象
        QTextStream stream(&file);
        
        // 设置写入文件的编码方式(默认情况下是根据平台默认的编码)
        stream.setCodec("UTF-8");
        
        // 向流中写数据
        stream << QString("主要看气质") << 250;
        
        // 关闭文件
        file.close();
    }
}

 

写入的文件为:

可以发现,写入的两个数据没有分隔,而是紧挨在一起的;这种情况下如果使用下面的方法读取数据,就无法读出正确的数据:

// 读文件
void Widget::on_btnReadFile_clicked()
{
    // 创建文件对象
    QFile file("file.txt");
    
    // 以只读方式打开文件
    if (file.open(QFile::ReadOnly))
    {
        // 创建文本流对象
        QTextStream stream(&file);
        
        // 设置写入文件的编码方式(默认情况下是根据平台默认的编码)
        stream.setCodec("UTF-8");
        
        QString str;
        int a;
        
        // 读取数据
        stream >> str >> a;
        qDebug() << str.toUtf8().data() << a;
        
        // 关闭文件
        file.close();
    }
}

输出结果如下:这是因为读取的数据全部给了变量 str,而整型变量 a 没有接收到数据,只有初始值 0.

 

六、QBuffer:表示一个内存缓冲区

QBuffer 类为 QByteArray 提供一个 QIODevice 接口。

QBuffer 允许我们使用 QIODevice 接口访问 QByteArray。QByteArray 被视为一个标准的随机访问文件。

QBuffer 相当于一个内存文件,也可以向其中读写数据:

    // 声明一个内存缓冲区(相当于一个内存文件,也可以读写数据)
    QBuffer buf;
    
    // 以只写方式打开内存缓冲区
    if (buf.open(QFile::WriteOnly))
    {
        // 向内存缓冲区写入数据
        buf.write("hello");
        buf.write("nihao");
        buf.write("how are you");
        
        buf.close(); // 关闭缓冲区
    }
    
    // 从缓冲区中读取数据
    // 可以看到,向内存缓冲区中写的多条数据,是紧挨在一起的
    qDebug() << buf.buffer();

QBuffer 可以和 QByteArray 一起使用,用 QByteArray 来存储 QBuffer 缓冲区中的数据:

    // 声明一个字节数组
    QByteArray array;
    
    // 声明一个内存缓冲区,指向一个字节数组;
    // 当向缓冲区中写入数据时,就是在向字节数组中写入数据;
    QBuffer buf(&array);
    
    // 以只写方式打开内存缓冲区
    if (buf.open(QFile::WriteOnly))
    {
        // 向内存缓冲区写入数据
        buf.write("hello");
        buf.write("nihao");
        buf.write("how are you");
        
        buf.close(); // 关闭缓冲区
    }
    
    // 从缓冲区中读取数据
    // 可以看到,向内存缓冲区中写的多条数据,是紧挨在一起的
    qDebug() << buf.buffer();
    qDebug() << "array:" << array;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值