Archive for September, 2016

Qt中的QDataStream

September 20th, 2016

之前在Java中遇到了需要序列化和反序列化的场景,Qt也为我们提供了类似的数据流类—QDataStream和QTextStream,可以从任意的输入输出设备读取或写入数据。其中QDataStream用于读写二进制数据,他可以在读写数据的时候已经严格定义了数据流的类型和每个类型的长度,不用关心编码转换的问题。TextStream用于读写文本(如HTML、XML和源代码等)的格式,解决了二进制文件格式无法直接阅读和编辑的缺点。QTextStream考虑了Unicode编码与系统本地编码或其他编码之间的转换问题,并考虑了不同操作系统之间行尾符切换的问题(MS “\r\n”, Mac “ \n”)。

QDataStream 与  QIODevice 联系十分紧密,QIODevice代表数据的输入输出媒介,我们拿 QFile 举例。从下面代码示例可以看到QDataStream通过QFile对文件进行修改

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

通过以上代码,我们也可以将复杂的QHash,QSet直接写入QDataStream中,然后再从文件中读出该数据结构,具体代码见main。通过查看Document,我们发现除了QFile,我们还可以把QAbstractSocket, QBuffer, QFile, QLocalSocket, QNetworkReply和QProcess传入QDataStream,进行数据的序列化保存。最令我惊讶的是QAbstractSocket和QProcess,也就是说我们对一个进程进行dump,类似于criu提供的快照功能,这个是非常强大的。

类似的Qt在序列化数据的时候也要设置setVersion(),out.setVersion(QDataStream::Qt_4_8); in.setVersion(QDataStream::Qt_4_8);这样在反序列化的时候不会出现数据格式的问题,类似于Java中的serialVersionUID。

另外一个与QDataStream结合比较紧密的就是QByteArray,我在编写网络程序中,会使用到QByteArray。这个class相比const char *更加强大,使用这个类也就意味着我们不用考虑字符串结束符\0,但是需要注意的是每次字符串赋值都是一次深拷贝,这样避免了出现内存问题。如果我们不需要深拷贝的时候,可以使用fromRawData ( const char * data, int size )直接对字符串进行修改。除此之外还可以使用resize方法对大小进行调整,这个在socket数据通信中十分有用。

QDataStream::QDataStream ( QByteArray * a, QIODevice::OpenMode mode )
Constructs a data stream that operates on a byte array, a. The mode describes how the device is to be used.Alternatively, you can use QDataStream(const QByteArray &) if you just want to read from a byte array.Since QByteArray is not a QIODevice subclass, internally a QBuffer is created to wrap the byte array.

 

参考:

http://doc.qt.io/qt-4.8/qbytearray.html

Qt下的QVariant类

September 12th, 2016

QVariant是一个数据类型类,存储不同类型的数据结构,一般情况下C++的class都不允许没有构造函数和析构函数存在。为了方便数据库对象存取,可以使用QVariant class。一个QVariant对象在一个时间内只保留一种类型的值。我们可以使用canConvert来查询是否能够转换当前的类型。转换类型一般以toT()命名。

QVariant可以保存很多Qt的数据类型,通过查看Document,可以知道发现支持很多Qt特定的数据类型,并且还有C++基本类型,如int、float等。QVariant还能保存很多集合类型,如QMap<QString, QVariant>, QStringList和QList<QVariant>等,因此QVariant完全满足现有编程需要。

今天在写代码过程中,尤其是读取数据库大量记录时,这个类足够好用。

struct s_field
{
      int type;
      QString fieldnum;
}
struct s_record
{
      QList<s_field> fieldlist;
      QString tableName;
}

struct s_recorddata
{
      QList<QVariant> datalist;
}
QList<s_recorddata> listdata;

通过这个数据结构我们可以把读到的数据放到listdata中,而每一个datalist都是一条记录,datalist中每一个的QVariant都是一个字段,我们不仅可以通过这个字段读取到数据,也可以判断具体的数据类型。

Type QVariant::type() const

Returns the storage type of the value stored in the variant. Although this function is declared as returning QVariant::Type, the return value should be interpreted as QMetaType::Type. In particular, QVariant::UserType is returned here only if the value is equal or greater than QMetaType::User.

Note that return values in the ranges QVariant::Char through QVariant::RegExp and QVariant::Font through QVariant::Transform correspond to the values in the ranges QMetaType::QChar through QMetaType::QRegExp and QMetaType::QFont through QMetaType::QQuaternion.

Pay particular attention when working with char and QChar variants. Note that there is no QVariant constructor specifically for type char, but there is one for QChar. For a variant of type QChar, this function returns QVariant::Char, which is the same as QMetaType::QChar, but for a variant of type char, this function returns QMetaType::Char, which is not the same as QVariant::Char.

Also note that the types void*, long, short, unsigned long, unsigned short, unsigned char, float, QObject*, and QWidget* are represented in QMetaType::Type but not in QVariant::Type, and they can be returned by this function. However, they are considered to be user defined types when tested against QVariant::Type.

To test whether an instance of QVariant contains a data type that is compatible with the data type you are interested in, use canConvert().

按照上面的数据结构和声明,只要listdata[i].datalist[j].type()就可以返回具体枚举类型,然后想返回具体值就是使用listdata[i].datalist[j].toInt() 或者其他的toxxx()即可。赋值的具体用法就是直接赋值即可。QVariant var; var = datatime …. var = 5 … var = 5.0 …

 

参考:

http://doc.qt.io/qt-4.8/qvariant.html#QVariant-2