数据流是编码信息的二进制流,它与主机计算机的操作系统,CPU或字节顺序无关。 例如,运行Windows的Sun SPARC可以读取Windows下PC写入的数据流。
您还可以使用数据流来读取/写入未编码的原始二进制数据。 如果要“解析”输入流,请参见QTextStream。
QDataStream类实现C ++基本数据类型的序列化,例如char,short,int,char *等。更复杂的数据的序列化是通过将数据分解为基本单元来实现的。
数据流与QIODevice紧密协作。 QIODevice表示一种输入/输出介质,可以从中读取数据或将数据写入其中。 QFile类是I/O设备的示例。
示例(将二进制数据写入流):
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
示例(从流中读取二进制数据):
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
写入流的每个项目均以预定义的二进制格式写入,该格式根据项目的类型而有所不同。支持的Qt类型包括QBrush,QColor,QDateTime,QFont,QPixmap,QString,QVariant等。有关支持数据流的所有Qt类型的完整列表,请参见序列化Qt数据类型。
对于整数,最好始终将其转换为Qt整数类型进行写入,然后再读回相同的Qt整数类型。这样可以确保获得所需大小的整数,并使您与编译器和平台的差异隔离。
枚举可以通过QDataStream进行序列化,而无需手动定义流运算符。枚举类使用声明的大小进行序列化。
举一个例子,一个char *字符串被写为一个32位整数,该整数等于包括'\0'字节的字符串的长度,其后是包括'\0'字节的字符串的所有字符。读取char *字符串时,将读取4个字节以创建32位长度值,然后读取char *字符串的许多字符,包括'\0'终止符。
初始I/O设备通常在构造函数中设置,但可以使用setDevice()进行更改。如果到达数据末尾(或没有设置I/O设备),则atEnd()将返回true。
版本控制
QDataStream的二进制格式自Qt 1.0以来已经发展,并且可能会继续发展以反映Qt中所做的更改。 输入或输出复杂类型时,确保使用相同版本的流(version())进行读写非常重要。 如果需要向前和向后兼容性,则可以在应用程序中对版本号进行硬编码:
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(MSB在前)。将其更改为Little Endian会破坏可移植性(除非读者也更改为Little Endian)。除非您有特殊要求,否则我们建议保留此设置。
读写原始二进制数据
您可能希望直接从数据流读取/写入自己的原始二进制数据。可以使用readRawData()将数据从流中读取到预分配的char *中。同样,可以使用writeRawData()将数据写入流中。请注意,数据的任何编码/解码都必须由您完成。
一对类似的函数是readBytes()和writeBytes()。它们与原始副本不同,如下所示:readBytes()读取一个Quint32,将其作为要读取的数据的长度,然后将该字节数读取到预分配的char *中; writeBytes()写入一个quint32,其中包含数据的长度,后跟数据。请注意,数据的任何编码/解码(长度quint32除外)都必须由您完成。
读写Qt集合类
Qt容器类也可以序列化为QDataStream。这些包括QList,QLinkedList,QVector,QSet,QHash和QMap。流运算符被声明为类的非成员函数。
读写其他Qt类
除了此处记录的重载流运算符之外,您可能要序列化为QDataStream的任何Qt类都将具有适当的流运算符,这些流运算符声明为该类的非成员函数:
QDataStream &operator<<(QDataStream &, const QXxx &);
QDataStream &operator>>(QDataStream &, QXxx &);
例如,以下是声明为QImage类的非成员的流运算符:
QDataStream & operator<< (QDataStream& stream, const QImage& image);
QDataStream & operator>> (QDataStream& stream, QImage& image);
若要查看您喜欢的Qt类是否定义了类似的流运算符,请查看该类的文档页面的“相关非成员”部分。
使用读取事务
当数据流在异步设备上运行时,数据块可能在任意时间点到达。 QDataStream类实现了一种事务处理机制,该机制提供了使用一系列流运算符自动读取数据的功能。 例如,您可以通过使用连接到readyRead()信号的槽函数中的事务来处理套接字的不完整读取:
in.startTransaction();
QString str;
qint32 a;
in >> str >> a; // try to read packet atomically
if (!in.commitTransaction())
return; // wait for more data
如果没有收到完整的数据包,此代码会将流恢复到初始位置,此后您需要等待更多数据到达。