QT中QDataStream二进制数据读写的实现

Qt中的QDataStream类为我们的程序提供了读写二进制数据的能力。一个数据流如果是二进制编码的数据流,那么它肯定是与计算机的操作系统、CPU或者字节序无关的。例如,一个数据流是在一个运行Windows系统的PC机上被写入的,那么它照样可以在一台运行Solaris的Sun SPARC的机器上被读取出来。同样,我们也可以使用QDataStream去读写原生的未编码的二进制数据。

QDataStream类实现了序列化C++的基本数据类型的功能,比如char,short,int,char* 等等。如果要序列化更复杂的数据类型,可以将复杂数据类型分解成独立的基本数据类型分别进行序列化。

一个数据流往往需要一个QIODevice配合使用。因为QIODevice代表了一个可以从中读取数据或向其写入数据的输入输出设备。我们最常常见的QFile文件类就是一种QIODevice。下面我们先分别看一个使用QDataStream进行二进制数据读写的例子。

write binary data to a stream:

  QFile file("file.dat");
  file.open(QIODevice::WriteOnly);
  QDataStream out(&file);   // we will serialize the data into the file
  out << QString("the answer is");   // serialize a string
  out << (qint32)42;        // serialize an integer

read binary data from a stream:

  QFile file("file.dat");
  file.open(QIODevice::ReadOnly);
  QDataStream in(&file);    // read the data serialized from the file
  QString str;
  qint32 a;
  in >> str >> a;           // extract "the answer is" and 42

每一项被写入的数据,都是按一种预定义的二进制格式写入的,改格式取决于具体每一项的类型。而QDataStream支持的类型包括QBrush,QColor,QDateTime等等。

特别注意,对应整数来说,在写入时最好转换成Qt中的某种整数类型,读取时也读取为同样的Qt整数类型。这可以确保得到正确大小的整数并且可以屏蔽掉不同编译器和平台之间的差异。举个栗子,对于一个char* 字符串来说,先写入一个32-bit的整数值,该值就是字符串的长度,包括'\0',紧接着是字符串中 的每一个字符,包括'\0'。当读取时,也是这样操作,写读取4字节创建出一个32-bit的字符串长度值,然后再根据创建出的长度值读取出相应个数的字符,包括'\0'。

版本

QDataStream的二进制格式从Qt1.0就开始形成了,很有可能在将来继续进化已反应Qt的变化。当操作复杂数据类型时,我们就要确保读取和写入时的QDataStream版本是一样的。如果你需要向前和向后兼容,可以在代码中使用硬编码指定流的版本号:

stream.setVersion(QDataStream::Qt_4_0);

如果你正在创建一种新的二进制数据格式,比如作为你的应用程序创建的文件的格式,你可以使用QDataStream以一种可移植的格式去写入这些数据。典型情况下,你可能会在文件头写入一个简短的幻数字符串和一个版本数字,来用于将来扩展。例如:

  QFile file("file.xxx");
  file.open(QIODevice::WriteOnly);
  QDataStream out(&file);

  // Write a header with a "magic number" and a version
  out << (quint32)0xA0B0C0D0;
  out << (qint32)123;

  out.setVersion(QDataStream::Qt_4_0);

  // Write the data
  out << lots_of_interesting_data;

那么,我们就可以以下面这种方式来读取:

  QFile file("file.xxx");
  file.open(QIODevice::ReadOnly);
  QDataStream in(&file);

  // Read and check the header
  quint32 magic;
  in >> magic;
  if (magic != 0xA0B0C0D0)
      return XXX_BAD_FILE_FORMAT;

  // Read the version
  qint32 version;
  in >> version;
  if (version < 100)
      return XXX_BAD_FILE_TOO_OLD;
  if (version > 123)
      return XXX_BAD_FILE_TOO_NEW;

  if (version <= 110)
      in.setVersion(QDataStream::Qt_3_2);
  else
      in.setVersion(QDataStream::Qt_4_0);

  // Read the data
  in >> lots_of_interesting_data;
  if (version >= 120)
      in >> data_new_in_XXX_version_1_2;
  in >> other_interesting_data;

同时,还可以在序列化数据时指定一个字节序。默认情况下是big endian。除非特殊需求,我们一个建议保持这个设置的默认值,不做修改。

读写原生二进制数据

有时,我们希望直接从data stream里读取原生的二进制数据。此时,可以使用readRawData() 将数据读入一个预先分配好的char*;同样的数据也可以使用writeRawData() 函数写入data stream。但要记住,使用这种方式的话,要由你自己进行所有数据的编码和解码。于此类似的另外两个函数是readBytes() 和 writeBytes()。这两个函数与上面的Raw版本相比,区别主要是:readBytes() 先读取一个quint32值,该值被当做将要读取的数据的长度,然后读取相应字节的数据到预先定义好的char*中;writeBytes() 也同理,先写入一个quint32的数据长度值,紧接着写入相应数据。同样,使用这两个函数,所以数据的编码和解码要有我们自己负责。注意,readBytes() 不需要我们事先分配好内存, 而readRawData() 需要我们事先分配好内存。

读写Qt集合类和其他Qt类

Qt的常见集合类也可以使用QDataStream进行序列化,这包括QList,QLinkedList,QVector,QSet,QHash和QMap。不过,序列化这些类的流操作符不是这个类的成员函数而已。同理,读写Qt中的其他类型,比如QImage,也是可以的。

使用事务

当在一个异步的设备上读取数据时,数据块可以在任意的时间点上到来。所以,为了应对这种情况,QDataStream提供了一个事务机制来确保原子性的完成一系列的流操作符。例如,你可以在操作socket时,在相应readyRead() 的槽函数中,使用事务来完成不完整的数据读取。

  in.startTransaction();
  QString str;
  qint32 a;
  in >> str >> a; // try to read packet atomically

  if (!in.commitTransaction())
      return;     // wait for more data

如果没有完整的数据包到来,commitTransaction() 会返回false,并将stream重置为初始状态,然后,等待更多数据的到来。

下面给出一个简单的测试程序:

#include <QCoreApplication>
#include <QDebug>
#include <QDataStream>
#include <QFile>
#include <QVector>
#include <QMap>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    //write
    QFile file("test.dat");
    if (!file.open(QIODevice::ReadWrite))
    {
        qDebug() << "open file failed";
        return 0;
    }
    QDataStream ds(&file);
    const char *wstr = "hello-world";
    quint32 wi = 1234;
    double wd = 1.1;
    float wf = 2.2f;
    QVector<int> wvector;
    wvector.push_back(1);
    wvector.push_back(2);
    wvector.push_back(3);
    QMap<int,int> wmap;
    wmap.insert(4, 4);
    wmap.insert(5, 5);
    wmap.insert(6, 6);
    ds << wstr;
    ds << wi;
    ds << wd;
    ds << wf;
    ds << wvector;
    ds << wmap;
    ds.writeBytes("file end ", qstrlen("file end "));
    ds.writeRawData("really end", qstrlen("really end"));

    //read
    file.seek(0);
    char *rstr;
    quint32 ri;
    double rd;
    float rf;
    QVector<int> rvector;
    QMap<int, int> rmap;
    char *rbytes;
    uint len;
    char *rraw = new char[100]{0};
    int rlen;
    ds >> rstr;
    ds >> ri;
    ds >> rd;
    ds >> rf;
    ds >> rvector;
    ds >> rmap;
    ds.readBytes(rbytes, len);
    ds.readRawData(rraw, rlen);
    qDebug() << rstr;
    qDebug() << ri;
    qDebug() << rd;
    qDebug() << rf;
    qDebug() << rvector;
    qDebug() << rmap;
    qDebug() << rbytes;
    qDebug() << rraw;

    return a.exec();
}

到此这篇关于QT中QDataStream二进制数据读写的实现的文章就介绍到这了,更多相关QT QDataStream二进制数据读写内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c++ qt自定义搜索编辑框的实现方法

    目录 自定义LineEdit 主界面代码 其它说明 实现效果如下: 实现方法说明:(1)自定义QLineEdit,在编辑框里添加布局,将按钮设置在右边(2)给自定义QLineEdit添加信号,当点击按钮搜索按钮时发送信号到主界面做相应的操作 自定义LineEdit csearchlineedit.h #ifndef CSEARCHLINEEDIT_H #define CSEARCHLINEEDIT_H #include <QLineEdit> #include <QPushButton&

  • C++11 写一个只触发一次槽函数的Qt connect函数

    目录 引言 ConnectionUtil.h: ConnectionUtil.cpp: 引言 在之前的Qt项目中,我发现经常会用到槽函数只需要执行一次的情况.也就是说,槽函数执行一次后,就需要disconnect对应的连接.然而,真正操作起来实际上挺麻烦的,或者说不优雅. 因为你需要把之前connect时产生的QMetaObject::Connection对象保存起来,而保存它不能用局部变量,通常需要保存到类的成员变量中,或者其他生命周期足够长的地方,以防止在disconnect它的时候,它已经

  • C++中的Qt QTableView详解

    目录 一.常用接口 1.设置model,添加model数据 2.节点可以带一些数据 3.一些别的常用设置 4.自动调整行高和列宽 5.设置表格不可编辑 6.设置行列头不显示 二.设置item属性 三.右键弹出菜单 四.源码 一.常用接口 1.设置model,添加model数据 model = new QStandardItemModel(this); model->setHorizontalHeaderLabels(QStringList()<<"姓名"<<

  • Qt多线程实现网络发送文件功能

    本文实例为大家分享了Qt多线程实现网络发送文件功能的具体代码,供大家参考,具体内容如下 客户端给服务器发送文件,服务器进行接收文件的简单操作 1. 服务器 1. 创建QTcpServer 类的对象 QTcpServer * server = new QTcpServer(this); 2. 进行监听 bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0) 3. 通过接收

  • C++ Qt QColorDialog使用方法

    目录 QColorDialog使用方法 getColor的调用 QPalette setColor Qt提供了颜色选择框,如下图: QColorDialog使用方法 例如下面的代码,点击按钮弹出颜色选择框,选择颜色,改变QLabel的背景色 #include "widget.h" #include "ui_widget.h" #include <QColorDialog> #include <QPalette> Widget::Widget(

  • C++ qt实现打开关闭状态按钮的代码

    效果图: 上述这种按钮,用QCheckBox可以实现,只要在选择与未选择的状态设置不同的图片即可:选择 未选择 实现代码 #include "widget.h" #include "ui_widget.h" #include <QMessageBox> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); ui->

  • PyQT5 emit 和 connect的用法详解

    对于PyQT4, PyQT5在一些使用上有着比较明显的变化有很大的变化,让人惊讶是在emit和connect上的一些变化比较有意思,相信也是QT为了更好的和Python相结合做的改进. 先上一张图: 出现 AttributeError: 'TCPWindow' object has no attribute 'connect' 这个问题说明了PyQT5不在支持PyQT4的链接信号槽方式! 对于emit使用如下: class Server(QTcpServer): updateServer= py

  • QT中QDataStream二进制数据读写的实现

    Qt中的QDataStream类为我们的程序提供了读写二进制数据的能力.一个数据流如果是二进制编码的数据流,那么它肯定是与计算机的操作系统.CPU或者字节序无关的.例如,一个数据流是在一个运行Windows系统的PC机上被写入的,那么它照样可以在一台运行Solaris的Sun SPARC的机器上被读取出来.同样,我们也可以使用QDataStream去读写原生的未编码的二进制数据. QDataStream类实现了序列化C++的基本数据类型的功能,比如char,short,int,char* 等等.

  • VBS中转换二进制数据为字符串常用办法

    至少有三种以上办法,可以把二进制数据(比如您从ASP的Request.BinaryRead方法得到的数据)转换为字符串. 第一种:使用VBS的MultiByte 方法 实例: Function SimpleBinaryToString(Binary)  'SimpleBinaryToString converts binary data (VT_UI1 | VT_ARRAY Or MultiByte string)  'to a string (BSTR) using MultiByte VBS

  • JavaScript读写二进制数据的方法详解

    前言 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数,如果想要在前端中处理音频和视频.那你必须要对二进制数据有很好地掌握和操作能力.下面话不多说了,来一起看看详细介绍的吧 类型化数组的出现 类型化数组是 HTML5 中引入的API,它能够让开发者使用 JavaScript 直接操作二进制数据.在类型化数组出现之前,我们是无法直接通过 JavaScript 操作二进制数据,通常都是操作 JavaScript 中的数据类型,由运行时转化成二进制.这就多了一个转化的过程,

  • 在ASP.NET 2.0中操作数据之五十三:在Data Web控件显示二进制数据

    导言: 在前面的教程我们阐述了应用程序处理二进制数据的2种模式,以及使用FileUpload 控件从浏览器向服务器文件系统上传文件.当文件上传并存储在文件系统里时,应在相应的数据库记录里存储该文件的存储路径. 我们先来看如何为最终用户提供二进制数据.怎样展示二进制数据呢?这取决于其类型.比如图片,我们将其显示为image:如果是PDFs,Microsoft Word文档.ZIP文件或其它类型的数据,或许提供一个"Download"链接比较妥当. 在本节,我们看如何在GridView和D

  • 详解XMLHttpRequest(二)响应属性、二进制数据、监测上传下载进度

    分析并操作 responseXML属性 如果你使用 XMLHttpRequest 来获得一个远程的 XML 文档的内容,responseXML 属性将会是一个由 XML 文档解析而来的 DOM 对象,这很难被操作和分析.这里有五种主要的分析 XML 文档的方式:  1.使用 XPath 定位到文档的制定部分.  2.使用 JXON 将其转换成 JavaScript 对象树.  3.手工的 解析和序列化 XML 为字符串或对象.  4.使用 XMLSerializer 把 DOM 树序列化成字符串

  • C++实现string存取二进制数据的方法

    本文实例讲述了C++实现string存取二进制数据的方法,分享给大家供大家参考.具体方法分析如下: 一般来说,STL的string很强大,用起来也感觉很舒服,这段时间在代码中涉及到了用string存取二进制数据的问题,这里记录一下,以供以后参考. 首先提一下STL中string的参考资料:http://www.cplusplus.com/reference/string/string/ ,不懂的朋友可以看下. 在数据传输中,二进制数据的buffer一般用系统预设的大数组进行存储,而不是STL的s

  • python实现在内存中读写str和二进制数据代码

    我就废话不多说了,还是直接看代码吧! # 利用python在内存中读写str和二进制数据 from io import StringIO from io import BytesIO f = StringIO() print(f.write('hello ')) # 6 print(f.write('world!')) # 6 print(f.getvalue()) # hello world! f = BytesIO() print(f.write('中文'.encode('utf-8')))

  • C/C++读写注册表中二进制数据(代码示例)

    1.RegOpenKeyEx 函数: 原形: LONG RegOpenKeyEx(               HKEY hKey,     // 要打开主键名               LPCTSTR lpSubKey, // 需要打开的子键或路径               DWORD ulOptions,  // 保留,为0              REGSAM samDesired, // 操作权限标志               PHKEY phkResult  // 指向你打开键

  • Qt基础开发之Qt文件操作类QFile读写文件的详细方法与实例及QDataStream的使用方法

    Qt文件操作类QFile简介 Qt中使用QFile类来操作文件的输入/输出.继承至QIODevice,QIODevice类是输入/输出设备的基类, 为设备提供了公共实现和抽象接口用于读写块数据.QIODevice又继承至QObject. 1.使用QFile类打开文件 QFile的构造函数 QFile(const QString &name) //传入一个文件路径 构造完成后,并没有打开文件,需要使用QFile::open函数来打开文件 [virtual] bool QFile::open(Ope

  • 使用node.js中的Buffer类处理二进制数据的方法

    前言 在Node.js中,定义了一个Buffer类,该类用来创建一个专门存放二进制数据的缓存区.这篇文章就详细介绍了node.js中的Buffer类处理二进制数据的方法,下面话不多说,来看看详细的介绍. 创建Buffer对象 第一种:直接使用一个数组来初始化缓存区 var arr = [0,1,2] var buf = new Buffer(arr) console.log(buf) 执行效果: 第二种:直接使用一个字符串来初始化缓存区 var str = 'hello' var buf = n

随机推荐