Obiekt klasy QString - zapis binarny

0

Cześć,

Mam w programie u siebie mapę przechowującą liczbę arabską jako klucz (int) i liczbę rzymską jako wartość (QString):

QMap<int,QString> ArabRzym;

Zawartość tej mapy chcę zapisać na dysku w pliku w postaci binarnej, używając podstawowej klasy std::fstream. Nie chcę używać klasy QFile, czy innej klasy z Qt, gdyż chcę się nauczyć uniwersalnej obsługi plików w C++ za pomocą biblioteki standardowej.

Oczywiście nie można zapisać sobie obiektu klasy QString w ten sposób...

// plik to obiekt klasy std::fstream
plik.write((char*)&lancuch,sizeof(QString));

...gdyż nic nie wiadomo o jego długości co może być problemem przy odczycie pliku i główną przyczyną segfaultów, bo jak wiadomo podczas odczytu łańcuchy mogą być różnej długości, a nie sizeof(QString).

Napisałem więc takie slociki do zapisu oraz do odczytu danych:

Zapis:

void OknoGlowne::zapisz(){
    std::fstream plik("liczby.dat",std::ios::out|std::ios::binary);
    if(plik.is_open()){

        //  Zapisanie ilości danych w mapie.
        int ilosc=this->ArabRzym.count();
        plik.write((char*)&ilosc,sizeof(int));

        int ar, dlRz; QString rz;
        for(QMap<int,QString>::Iterator it=this->ArabRzym.begin();it!=this->ArabRzym.end();++it){
            ar=it.key(); rz=it.value(); dlRz=rz.length();

            //  Zapisanie liczby arabskiej i długości łancucha liczby rzymskiej.
            plik.write((char*)&ar,sizeof(int));
            plik.write((char*)&dlRz,sizeof(int));

            //  Zapisanie liczby rzymskiej znak po znaku.
            QChar temp;
            for(int i=0;i<dlRz;i++){
                temp=rz[i];
                plik.write((char*)&temp,sizeof(QChar));
            }
        }

        plik.close();
    }
}

Odczyt:

void OknoGlowne::odczytaj(){
    this->ArabRzym.clear();

    std::fstream plik("liczby.dat",std::ios::in|std::ios::binary);
    if(plik.is_open()){

        //  Odczytanie ilości danych dla mapy.
        int ilosc;
        plik.read((char*)&ilosc,sizeof(int));

        int ar, dlRz; QString rz;
        for(int i=0;i<ilosc;i++){

            //  Odczytanie liczby arabskiej i długości łancucha liczby rzymskiej.
            plik.read((char*)&ar,sizeof(int));
            plik.read((char*)&dlRz,sizeof(int));

            //  Odczytanie liczby rzymskiej znak po znaku.
            QChar temp;
            for(int j=0;j<dlRz;j++){
                plik.read((char*)&temp,sizeof(QChar));
                rz.append(temp);
            }
            this->ArabRzym.insert(ar,rz);
            rz.clear();
        }

        plik.close();

    //  Jakaś funkcja do wypełniania tabeli w programie
        this->wypelnij();
    }
}

Moje pytanie: Czy za pomocą samej tylko klasy std::fstream da się 'ładniej' zapisać do pliku binarnego obiekt klasy QString?

Obecny format pliku z danymi:


ilość danych | (liczba arabska | długość łańcucha liczby rzymskiej | łańcuch liczby rzymskiej) * ilość danych

Pozdrawiam
Grzesiek

0
 plik.write((char*)&ar,sizeof(int));
 plik.write((char*)&dlRz,sizeof(int));
//[...]
 plik.read((char*)&ar,sizeof(int));
 plik.read((char*)&dlRz,sizeof(int));

To nie zadziała.

bufor znakowy i sprintf (najłatwiej), lub zapisywanie znak po znaku (log(n), aby określić ilość cyfr, ew. zapisywanie do bufora i wczytanie do pliku buforu od końca - dzielisz sukcesywnie na 10).
http://www.cplusplus.com/reference/cstdio/sscanf/
http://www.cplusplus.com/reference/cstdio/sprintf/

plik.write((char*)&temp,sizeof(QChar));

Nie wiem jak wygląda QChar - czy to obiekt, czy alias dla char, ale jeśli to nie alias, to ten zapis też nie zadziała.

Zamień:

QChar temp;
            for(int i=0;i<dlRz;i++){
                temp=rz[i];
                plik.write((char*)&temp,sizeof(QChar));
            }

na:

plik<<rz.toStdString();
0

Ale kiedy to właśnie działa i to bardzo ładnie. I to w dodatku wszystkie części tych moich funkcji bardzo ładnie działają :) Pytałem tylko czy nie da się zapisu stringu jakoś uprościć, żeby nie rozbijać go na pojedyncze znaki czyli QChar. QChar to obiekt, z którego składają się znaki QStringa. QChar to szesnastobitowy obiekt kodowany w Unicode. Taki zamiennik char'a można prosto powiedzieć.

Mało tego, próbowałem inne obiekty zapisywać w ten sposób np. złożone struktury itp i zawsze bardzo ładnie to działało, zarówno zapis jak i odczyt:)

Jak zrobię tak:

plik<<rz.toStdString();

to nie będę znał długości tego stringa, ale fakt... mogę zapisać sobie jego długość przed samym obiektem.

0

to co napisał Grzesiek jest jak najbardziej prawidłowe. QChar jest po prostu wrapperem wokół typy wbudowanego unsigned short z dodatkowymi metodami do obsługi unicode.
Podejście do zapisu jest prawidłowe i nie da się tego uprościć inaczej niż stosując klasy Qt: QDataStream i QFile.
Zresztą QDataStream robi dokładnie to samo co Grzesiek tyle, że uwzględnienia jeszcze architekturę i/lub platformę (endiana, standard zapisu liczb zmiennoprzecinkowych oraz 32/64 bity).
http://qt-project.org/doc/qt-5.0/qtcore/qdatastream.html
http://qt-project.org/doc/qt-5.0/qtcore/datastreamformat.html

Jedynie co można poprawić to wywalić pętlę po znakach i zapisywać je hurtem, bo jest to jeden kawałek pamięci.
http://qt-project.org/doc/qt-5.0/qtcore/qstring.html#constData

plik.write((const char*)rz.constData(), sizeof(QChar)*rz.size());

1 użytkowników online, w tym zalogowanych: 0, gości: 1