Obiekt klasy QString - zapis binarny

Obiekt klasy QString - zapis binarny
grzesiek51114
grzesiek51114
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:2442
0

Cześć,

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

Kopiuj
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...

Kopiuj
// 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:

Kopiuj
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:

Kopiuj
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:

Kopiuj

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

Pozdrawiam
Grzesiek

edytowany 4x, ostatnio: grzesiek51114
Ola Nordmann
  • Rejestracja:prawie 12 lat
  • Ostatnio:ponad 8 lat
  • Postów:414
0
Kopiuj
 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/

Kopiuj
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ń:

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

na:

Kopiuj
plik<<rz.toStdString();

<img src="http://scontent-a-vie.xx.fbcdn.net/hphotos-ash3/1379478_311850692288742_1730250652_n.jpg" />
Geniusz zua :>
edytowany 6x, ostatnio: Ola Nordmann
grzesiek51114
grzesiek51114
  • Rejestracja:ponad 11 lat
  • Ostatnio:ponad 4 lata
  • Postów:2442
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:

Kopiuj
plik<<rz.toStdString();

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

edytowany 2x, ostatnio: grzesiek51114
Ola Nordmann
rz.toStdString().length();
grzesiek51114
grzesiek51114
W porządku, to znam. Zastanawiam się tylko dlaczego napisałeś, że nie zadziała ten sposób plik.write((char*)&obiekt,sizeof(typ)); :)
Ola Nordmann
ofstream może sobie rzutować inty, floata i inne typy wbudowane. Do tego może też rzutować typy biblioteki std, ale bez dodatkowego przeciążenia (a wątpię, żeby QT takie tworzyło) nie zapisze dowolnego typu, bo nie będzie wiedział jak go zinterpretować.
grzesiek51114
grzesiek51114
Nie rozumiem tego co napisałeś o przeciążeniu. Przecież spokojnie zapisuję za pomocą tegoż sposobu swoje obiekty swoich własnych klas, i obiekty z Qt także. Problem był tylko ze stringami. Gdyby tak nie było to nie byłbym w stanie zapisać sobie określonej ilości obiektów typu QChar. Może głupio wygląda to co piszę ale nie rozumiem tego co Ty napisałeś.
MarekR22
Moderator C/C++
  • Rejestracja:ponad 17 lat
  • Ostatnio:3 minuty
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

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

Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 3x, ostatnio: MarekR22
grzesiek51114
grzesiek51114
Kamień z serca. Chociaż na 95% myślałem, że to jest prawidłowy zapis to jednak kolega z postu wyżej wprawił mnie w zakłopotanie.
MarekR22
coś mu się pokićkało, chyba nie doczytał, że to ma być zapis binarny, bo wyskoczył ze scanf-em.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.