QDataStreamを使用する

私はよくQDataStreamクラスを使用する必要がありました。 その結果、私はそれを正しく使用する方法についていくつかの経験を得ました。





はじめに



クラスでの作業の開始時に、クラスには名前にストリームがあるため、単にデータを保存する義務があるという意見があることに繰り返し気付きました。 ヘルプの行はQDatastream :: QDatastream(QByteArray * a、QIODevice :: OpenMode mode)です。

バイト配列で動作するデータストリームを構築します。 モードは、デバイスの使用方法を記述します。 最初は、心配する人はほとんどいません。 しかし、内部を見ると、データがQDataStreamに直接書き込まれていないことがわかります。 コンストラクターはQBufferクラスを初期化します。この場合、このクラスは渡されたQByteArrayのラッパーです。 考え方は次のとおりです。データはQByteArrayに排他的に格納され、(デ)シリアル化のすべての操作はQDataStreamクラスによって実行されます。 ストリームからデータを読み取る場合、現在のバイトへのポインターのみが変更され、データ自体は失われません。 書き込み時に、 QIODevice :: WriteOnlyのデータ新しい値で上書きされ、 QIODevice :: Appendのデータは最後に追加されます。 これから、QByteArrayのライフタイム制御に関する結論が得られます。



読み取り書き込み



レコードは、すべてのメインタイプに対して定義された標準演算子<<によって表されます。 ただし、データをデータ構造に直接書き込む方が便利な場合がよくあります。 次の例は、目的のために<<演算子をオーバーロードする方法を示しています。



sctruct anyStruct { short sVal; float fVal; double dVal; short Empty; char array[8]; } QDataStream operator <<(QDataStream &out, const anyStruct &any) { out << any.sVal; out << any.fVal; out << any.dVal; out << any.Empty; out.writeRawData(any.array,sizeof(any.array)); return out; }
      
      







ここでは、すべてが非常に単純です。「複雑な」構造のレコードは、より単純なタイプのレコードに分割されます。 QDataStream writeRawData(const char * s、int len)にも注意してください 。 配列の値をループで書き込むことは可能ですが、よりエレガントな方法がある場合はなぜそうするのでしょう。



 QDataStream operator >>(QDataStream &out, anyStruct &any) { out >> any.sVal; out.setFloatingPointPrecision(QDataStream::FloatingPointPrecision); out >> any.fVal; out.setFloatingPointPrecision(QDataStream::DoublePrecision); out >> any.dVal; out.skipRawData(sizeof(any.Empty)); out.ReadRawData(any.array,sizeof(any.array)); return out; }
      
      







ここではすべて同じですが、関数QDataStream :: setFloatingPointPrecision(FloatingPointPrecision precision)に注意する必要があります。 実際のところ、Qt 4.6以降では、浮動小数点型の精度を明示的に指定する必要があります。 ご想像のとおり、単精度の型にはSinglePrecisionが必要であり、倍精度の型にはDoublePrecisionが必要です。 この不快な状況を解決する方法は2つあります。最初の方法は、<<および>>を次のようにリロードすることです



 QDataStream operator >>(QDataStream &out, float &val) { if(out. FloatingPointPrecision() != QDataStream:: SinglePrecision) { out.setFloatingPointPrecision(QDataStream::FloatingPointPrecision); out >> val; out.setFloatingPointPrecision(QDataStream::DoublePrecision); } }
      
      







または、コードで、floatを読み取る前に指定し、それらの読み取り方法を2倍にします。 デフォルトでは、DoublePrecisionが使用されます。

QDataStream :: skipRawData(int len)に注目しましょう。 この関数は、指定されたバイト数をスキップするだけです。これは、構造を整列させるときに非常に便利です。



それとは別に、上位ビットを記録する順序についても言及する必要があります。 QDataStream :: setByteOrder(ByteOrder bo)メソッドは順序を設定します。 ByteOrder :: BigEndianを使用すると、レコードのバイトが高くなり、これがデフォルトの順序になります。 ByteOrder :: LittleEndianを使用すると、レコードの最下位ビットが先に進みます。



(De)Qtクラスのシリアル化



QDataStreamでは、標準のC ++型に加えて、QListやQVariantなどのQtクラスを作成することもできます。 ただし、Qtバージョンに関連するいくつかの問題が隠されています。 ただし、開発者は異なるバージョンのクラスのシリアル化を処理しました。 QDataStream :: setVersion(int v)メソッド vはQtのバージョン)がこれを担当します。 それにもかかわらず、可能であれば、異なるバージョンを介してクラスをドラッグするには、ライブラリの現在のバージョンにあるクラスのプロパティのみが利用可能であることを覚えておく価値があります。 QDataStream :: version()を使用して、ストリームが動作するバージョンを取得できます。 QHashコンテナファイルへの書き込みの小さな例を考えてみましょう。



 #include <QtCore/QCoreApplication> #include <QDataStream> #include <QByteArray> #include <QFile> #include <QString> #include <QHash> #include <QDebug> class simpleClass { public: quint32 a,b; quint32 func(quint32 arg1, quint32 arg2); quint32 getC(); friend QDataStream &operator <<(QDataStream &stream,const simpleClass &sC); friend QDataStream &operator >>(QDataStream &stream, simpleClass &sC); protected: quint32 c; }; inline quint32 simpleClass::func(quint32 arg1, quint32 arg2) { a = arg1; b = arg2; c = a+b; return c; } inline quint32 simpleClass::getC() { return c; } inline QDataStream &operator <<(QDataStream &stream,const simpleClass &sC) // ; { stream << sC.a; stream << sC.b; stream << sC.c; return stream; } inline QDataStream &operator >>(QDataStream &stream, simpleClass &sC) // ; { stream >> sC.a; stream >> sC.b; stream >> sC.c; return stream; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QFile appFile(QString("filename.data")); appFile.open(QFile::Append); //    ; QDataStream inFile(&appFile); //     QIODevice; QHash <quint32,simpleClass> hash; //       ; for(quint32 i = 0; i < 64; i++) { if(!hash.contains(i)) //    ,    ; { simpleClass sC; //  ; sC.func(i,i+10); //   ; hash.insert(i,sC); //   ; } } inFile.setVersion(QDataStream::Qt_4_8); //    Qt,  ; inFile << hash; //  ; appFile.flush(); //     ; appFile.close(); //      ; QFile readFile(QString("filename.data")); readFile.open(QFile::ReadOnly); QDataStream outFile(&readFile); outFile.setVersion(QDataStream::Qt_4_8); QHash<quint32,simpleClass> readHash; //   ; outFile >> readHash; //     ; foreach(quint64 key, readHash.keys()) //   : readHash.keys()    ; { simpleClass sC = readHash.value(key); //     ; qDebug() << "Sum was " << sC.getC(); //   ; qDebug() << "Sum is "<< sC.func(key,2*key); //   ; } readFile.close(); return a.exec(); }
      
      







例に移る前に、flush()などに特別な注意を払いたいと思います。 一部のデバイスで作業する場合、データをバッファリングできます。つまり、書き込み関数の呼び出し時にデバイスへの書き込みが発生しない場合があります。 バッファリングのトピックは別の記事に値するので、記録のためにバッファを強制的に空にしなければならないという結論に絞ってみましょう。



この例では、simpleClassクラスを作成し、そのクラスのQDataStream演算子をオーバーロードしました。 プライベートセクションに直接アクセスできるように、演算子をクラスフレンドリーにしていることに注意してください。 クラスの演算子をオーバーロードしたり、プライベートプロパティにアクセスするための関数を記述したりして、庭をフェンスすることは可能ですが、個人的には、友人とのソリューションはよりエレガントに思えます。 その後、すべてが非常に簡単です。Qtのバージョンを指定することを忘れずに、読み取り用にファイルを開き、ファイルバインディングでストリームを作成し、QHashに入力して書き込みます。 読み上げの順序は実質的に違いはありません。



おわりに



高度なクラス設計にもかかわらず、心に留めておくべき落とし穴もあります。 クラスはデータ構造の機能的なラッパーであり、読み取り/書き込み操作を直接実行する必要がある場合に使用し、他の手段を使用してデータを転送することは理にかなっています。 さらに、良好なトーンは、ストリームの処理のパラメーターを明示的に示します。 数行は、あなたの後にコードを操作しなければならない人のために、将来多くの神経を節約します。



All Articles