QDataStream


對於純綷的二進位資料,可以使用QDataStream來協助處理,可以直接處理C++基本資料型態、還有許多Qt資料型態,像是QByteArray、QString、QMap等,可以使用 << 或 >> 運算子來進行資料輸出或寫入。

先使用以下的簡單例子,示範一下QDataStream搭配QFile來進行檔案讀寫:
#include <QFile>
#include <QDataStream>
#include <QString>
#include <QMap>
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
QFile file("data.dat");

QMap<QString, int> map;
map.insert("caterpillar", 95);
map.insert("momor", 93);

if(!file.open(QIODevice::WriteOnly)) {
cerr << "Cannot open file for writing: "
<< qPrintable(file.errorString()) << endl;
return false;
}

QDataStream out(&file);
// 設定QDataStream支援版本
out.setVersion(QDataStream::Qt_4_3);

// 寫入資料
out << 1 << map;
file.close();

if(!file.open(QIODevice::ReadOnly)) {
cerr << "Cannot open file for reading: "
<< qPrintable(file.errorString()) << endl;
return false;
}

QDataStream in(&file);
// 設定QDataStream支援版本
in.setVersion(QDataStream::Qt_4_3);

int num = 0;
QMap<QString, int> inMap;

// 讀入資料
in >> num >> inMap;

cout << "num: " << num << endl
<< "map: <caterpillar, " << inMap.value("caterpillar") << ">" << endl
<< "map: <momor, " << inMap.value("momor") << ">" << endl;

return true;
}

程式中可以看到setVersion()方法,這設定QDataStream讀寫時的版本,因為Qt的物件成員等資料,會隨著
不同版本而可能有所不同,例如QMap新版中可能有一些成員屬性是舊版本所沒有的,使用setVesrion()設定Qt支援的讀寫版本,告訴QDataStream在寫入或讀取時應當處理的物件資料。

程式執行時的結果如下所示:

num: 1
map: <caterpillar, 95>
map: <momor, 93>


QDataStream也可以直接處理位元資料,例如使用 readRawBytes()與writeRawBytes()來進行原始位元資料的處理。QDataStream處理數值時,預設使用big- endian的方式,如果您要改變為使用little-endian,則可以使用setByteOrder()方法設定為QDataStream:: LittleEndian。

如果想要QDataStream可以使用 << 或 >> 來支援您的自訂義物件,則您需要重載 << 與 >> 運算子,告訴QDataStream如何儲存或讀取物件,例如:
#include <QFile>
#include <QDataStream>
#include <QString>
#include <iostream>
using namespace std;

class Dog {
public:
Dog() { _number = 0; }

Dog(int number, const QString &name) {
_number = number;
_name = name;
}

void setNumber(int number) { _number = number; }
int number() const { return _number; }

void setName(const QString &name) { _name = name; }
QString name() const { return _name; }

private:
int _number;
QString _name;
};

QDataStream &operator<<(QDataStream &out, const Dog &dog) {
out << dog.number() << dog.name();
return out;

}

QDataStream &operator>>(QDataStream &in, Dog &dog) {
int number = 0;
QString name;

in >> number >> name;

dog.setNumber(number);
dog.setName(name);

return in;
}


int main(int argc, char *argv[]) {
QFile file("data.dat");

Dog dog1(1, "caterpillar");
Dog dog2(2, "momor");

if(!file.open(QIODevice::WriteOnly)) {
cerr << "Cannot open file for writing: "
<< qPrintable(file.errorString()) << endl;
return false;
}

QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_3);
out << dog1 << dog2;
file.close();

if(!file.open(QIODevice::ReadOnly)) {
cerr << "Cannot open file for reading: "
<< qPrintable(file.errorString()) << endl;
return false;
}

QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_3);

in >> dog1 >> dog2;

cout << dog1.number() << ", " << qPrintable(dog1.name()) << endl
<< dog2.number() << ", " << qPrintable(dog2.name()) << endl;

return true;
}

程式執行時的結果如下所示:
1, caterpillar
2, momor


如以上重載 << 與 >> 運算子,還有一個好處,就是可以讓自定義物件支援QList容器之 << 與 >> 之附加與取出,例如像以下的操作:
QList<Dog> list;
   
Dog dog1(1, "caterpillar");
Dog dog2(2, "momor");
   
list << dog1 << dog2;
   
QList<Dog>::const_iterator iterator = list.begin();
   
while(iterator != list.end()) {
    cout << (*iterator).number() << ", " 
         << qPrintable((*iterator).name()) << endl;
    ++iterator;
}