speichern von einem Vektor von Objekten auf der Festplatte



  • Hi!

    Danke bis hierher.

    Es handelt sich genauer um einen Vector mit diesen struct als Vektorgrößen:

    struct RoiDbStruct
    {
        QString name;
        Mat ;
        vector<Vec2f>;
        vector<Vec2i>;
        vector<Vec3f>;
        vector<Vec4i>;
        vector< vector< Point> >;
        vector<KeyPoint>;
        Mat ;
        vector<KeyPoint>;
        Mat;
        vector<KeyPoint>;  
        Mat ;
        vector<KeyPoint> ; 
    };
    

    Mat und KeyPoint sind Klassen von OpenCV.
    Einfach ist dieses Struct nicht.
    Sie ist einfach dahingehend, dass es keine direkt vererbten Klassen sind.
    Am liebsten wäre es mir natürlich wenn ich den Structvektor so wie er ist speichern könnte und ihn auf einer anderen Plattform wieder lesbar machen kann... Vll gibt es ja was von Qt5 (meine IDE)?

    Danke nochmal!



  • Ingeborg schrieb:

    Vll gibt es ja was von Qt5 (meine IDE)?

    Qt bietet die Möglichkeit, eigene Datentypen zu serialisieren. Ich weiß nicht, wie das bei fremden Klassen ist.



  • In Qt läuft sowas z. Bsp. über QDataStream. Diese Klasse bietet portables binäres lesen und schreiben für viele Datentypen (C++ Standard und viele Qt Klassen). Bei eigenen Klasse muss man sich die dafür notwendigen Funktionen selber schreiben.
    Ein bisschen was dazu steht auch in der Hilfe zu QDataStream.



  • Ingeborg schrieb:

    Hi!

    Danke bis hierher.

    Es handelt sich genauer um einen Vector mit diesen struct als Vektorgrößen:

    struct RoiDbStruct
    {
        QString name;
        Mat ;
        vector<Vec2f>;
        vector<Vec2i>;
        vector<Vec3f>;
        vector<Vec4i>;
        vector< vector< Point> >;
        vector<KeyPoint>;
        Mat ;
        vector<KeyPoint>;
        Mat;
        vector<KeyPoint>;  
        Mat ;
        vector<KeyPoint> ; 
    };
    

    Mat und KeyPoint sind Klassen von OpenCV.
    Einfach ist dieses Struct nicht.
    Sie ist einfach dahingehend, dass es keine direkt vererbten Klassen sind.
    Am liebsten wäre es mir natürlich wenn ich den Structvektor so wie er ist speichern könnte und ihn auf einer anderen Plattform wieder lesbar machen kann... Vll gibt es ja was von Qt5 (meine IDE)?

    Danke nochmal!

    Hallo Ingeborg,

    jetzt hat Dir noch keiner verraten, wie es geht - oder?
    Die Grundidee ist es, für jeden Typ (struct oder class) einen sogenannten Streamingoperator zu schreiben. Ziel ist es alles in texttueller Form zu speichern und zu lesen. Das mag nicht die schnellste Form sein, aber erstens ist man kompatibel mit allen möglichen anderen Systemen und zum anderen kann man mit Hilfe eines einfachen Editors die Ausgabe kontrollieren.

    Für die Struktur RoiDbStruct heißt das:

    std::ostream& operator<<( std::ostream& out, const RoiDbStruct& obj );
    

    für das Schreiben und

    std::istream& operator>>( std::istream& in, RoiDbStruct& obj );
    

    für das Lesen.

    Dann wird folgendes möglich:

    RoiDbStruct obj;
        // obj mit Informationen füllen
        std::ofstream output( "SaveRoiDbStruct.txt" );
        if( output << obj )
            std::cout << "Speichern Ok" << std::endl;
    

    .. und umgekehrt das Lesen:

    RoiDbStruct obj;
        std::ifstream input( "SaveRoiDbStruct.txt" );
        if( input >> obj )
            std::cout << "Laden Ok" << std::endl;
    

    wie werden diese Streamingoperatoren implementiert - nun indem 'einfach' alle Member abgespeichert, bzw. gelesen werden:

    struct RoiDbStruct
    {
        QString name;
        Mat mat1;
        vector<Vec2f> vec1;
        vector<Vec2i> vec2;
        vector<Vec3f> vec3;
        vector<Vec4i> vec4;
        vector< vector< Point> > points;
        vector<KeyPoint> keypoints1;
        Mat mat2;
        vector<KeyPoint> keypoints2;
        Mat mat3;
        vector<KeyPoint> keypoints3;
        Mat mat4;
        vector<KeyPoint> keypoints4; 
    };
    
    std::istream& operator>>( std::istream& in, RoiDbStruct& obj )
    {
        in >> obj.name >> obj.mat1 >> obj.vec1 >> obj.vec2 ... // usw.
        return in;
    }
    

    Die Ausgabe entsprechend.

    Damit das funktioniert ist für jeden Membertypen ebenso ein Streamingoperatorenpaar zu schreiben. Für QString ist das noch relativ einfach, wenn ich mal annehmen, dass dort nur ASCII-Zeichen hinterlegt sind, und kein Zeilenumbruch (EOL).

    Schreiben:

    std::ostream& operator<<( std::ostream& out, const QString& s )
    {
        return out << s.toStdString() << "\n"; // EOL beendet den String
    }
    

    Lesen:

    std::istream& operator>>( std::istream& in, QString& s )
    {
        std::string line;
        if( std::getline( in, line ) ) // Zeile gelesen?
            s = line.c_str();
        return in;
    }
    

    Den Typ Mat lasse ich hier mal weg - Vec2f und die weiteren Vec*-typen sollte einfach sein:

    std::ostream& operator<<( std::ostream& out, const Vec2f& v )
    {
        return out << v.x << " " << v.y;
    }
    std::istream& operator>>( std::istream& in, Vec2f& v )
    {
        float x, y;
        if( in >> x >> y )
            v = Vec2f( x, y );
        return in;
    }
    

    Einen std::vector<T> abzuspeichern geht im Prinzip genauso, nur dass Du Dich um die Anzahl der Elemente kümmern musst. Das könnte allgemein so aussehen:

    template< typename T >
    std::ostream& operator<<( std::ostream& out, const std::vector<T>& v )
    {
        out << v.size() << "\n"; // die Anzahl
        for( auto i = begin(v); i != end(v); ++i )
            out << *i << "\n";
        return out;
    }
    template< typename T >
    std::istream& operator>>( std::istream& in, std::vector<T>& v )
    {
        std::vector<T>::size_type n;
        if( in >> n ) // Anzahl der Elemente
        {
            std::vector<T> result;
            result.reserve(n);
            T obj;
            for( std::vector<T>::size_type i=0; i<n && in >> obj; ++i )
                result.push_back( obj );
            if( in )
                v = std::move( result );       
        }
    }
    

    usw. - wenn man das Prinzip verstanden hat, ist es mehr eine Fleißarbeit, als ein Problem.
    Wenn Du mehr wissen möchtest, dann frage noch mal nach.

    Gruß
    Werner


  • Mod

    Wenn du schon C++11 verwendest, Werner, warum nicht gleich so:

    for( auto i = begin(v); i ! end(v); ++i )
        out << *i << "\n";
    // =>
    for( auto const& i : v )
        out << i << "\n";
    

    Für QString ist das noch relativ einfach, wenn ich mal annehmen, dass dort nur ASCII-Zeichen hinterlegt sind, und kein Zeilenumbruch (EOL).

    EOL ist auch Teil von ASCII (LF, CR, CRLF)... (?)
    Die Annahme ist außerdem nicht zu rechtfertigen, wenn man das ganze allgemein implementieren will, wie du gezeigt hast. Da müsste man eine allgemeine Funktion für Container implementieren, die es macht wie du für vector in deinem Post.



  • Arcoth schrieb:

    Wenn du schon C++11 verwendest, Werner, warum nicht gleich so:

    .. weil mein Compiler das nicht kann 😉

    Arcoth schrieb:

    Für QString ist das noch relativ einfach, wenn ich mal annehmen, dass dort nur ASCII-Zeichen hinterlegt sind, und kein Zeilenumbruch (EOL).

    EOL ist auch Teil von ASCII (LF, CR, CRLF)... (?)
    Die Annahme ist außerdem nicht zu rechtfertigen, wenn man das ganze allgemein implementieren will, wie du gezeigt hast. Da müsste man eine allgemeine Funktion für Container implementieren, die es macht wie du für vector in deinem Post.

    ist ja alles richtig, was Du sagst, aber ich wollte es nicht zu kompliziert machen.


  • Mod

    ist ja alles richtig, was Du sagst, aber ich wollte es nicht zu kompliziert machen.

    Verständlich. 👍

    .. weil mein Compiler das nicht kann

    Dieser ver******* VC++ 😡



  • Arcoth schrieb:

    Dieser ver******* VC++ 😡

    beruhig dich, brauner.



  • Oh vielen Dank,

    ich dachte, dass ebenjene in und outstreamen eben nicht funktioniert. Vielen Dank! Das erleichtert mir einiges.

    Grüße!
    Inge



  • otze schrieb:

    Arcoth schrieb:

    Dieser ver******* VC++ 😡

    beruhig dich, brauner.

    Also mein Visual Studio kann sowohl Range-based for Schleifen als auch einige andere C++11-Features...

    Kann doch sein, dass Werner einen alten GCC oder so benutzt hat. Wieso immer gleich gegen MSVC?


Anmelden zum Antworten