Zapis/czytanie struktury do/z pliku

0

Witam, mam problem z czytaniem i zapisem struktur z i do pliku.

Działanie programu:
Program wczytuje listę schematów z pliku, ładuje okno formularza, dodaje listę schematów do 'comboBox', po wypełnieniu formularza dopisuje do pliku nowy schemat.

Problem:
Gdy odpalę program i zapiszę do pustego pliku binarnego strukturę a potem ją odczytam to program wykona to prawidłowo. Gdy dopiszę kolejną to program odczyta ostatni rekord prawidłowo a pierwszy przeczyta źle(printuje jakiś ciąg znaków), gdy dodam trzecią to program się wysypuje przy odczycie wyrzucając taki błąd:

"ASSERT: "size == 0 || offset < 0 || size_t(offset) >= sizeof(QArrayData)" in file"

Dodatkowo gdy wyłączę program np. po dodaniu pierwszego rekordu i uruchomię ponownie to podczas czytania pliku program od razu się wysypuje. A nie powinien bo w trakcie dodawania czyta go poprawnie czyli powinien po odpaleniu programu znowu go przeczytać poprawnie.

Konsola:

Uruchamiam program
Czytam "blueprint.bin"
QFileDevice::seek: IODevice is not open
Rozmiar pliku  0  ilość Danych  0

Dodawanie schematu
Zapisuje do  "blueprint.bin"

Pobieranie schematu
Czytam "blueprint.bin"
Rozmiar pliku  40  ilość Danych  1
"Pierwszy record"

//----- Koniec pierwszego przebiegu ----//

Dodawanie schematu
Zapisuje do  "blueprint.bin"

Pobieranie schematu
Czytam "blueprint.bin"
Rozmiar pliku  80  ilość Danych  2
"\u0080"                  <<--- Pierwszy record źle odczytywany
"Drugi record"
//----- Koniec drugiego przebiegu ----//

Dodawanie schematu
Zapisuje do  "blueprint.bin"
Czytam "blueprint.bin"

Pobieranie schematu
Rozmiar pliku  120  ilość Danych  3
Program nieoczekiwanie przerwał pracę. <<--- Error przy czytaniu pliku

Struktura:

// sizeof(Article) = 40;

typedef struct {
   unsigned int id;
   QString articleNo;
   QString articleName;
   unsigned int Amount;
   float lSize, qSize, height;
   unsigned int lAmount, qAmount;
   QString paper;
} Article;

Zapis:


    qDebug() << "Zapisuje do " <<_path;
    QFile temp(_path);

    temp.open(QIODevice::Append);
    temp.write((const char *) (&article), sizeof(Article));
    temp.close();

    return true;

Odczyt i printowanie:


    qDebug() << "Czytam" <<_path;
    QFile temp(_path);
    temp.open(QIODevice::ReadOnly);
    temp.seek(0);

    QDataStream in(&temp);
    in.setVersion(QDataStream::Qt_5_5);

    qDebug() << "Rozmiar pliku " << temp.size() << " ilość Danych " << temp.size() / sizeof(Article);

    while (!temp.atEnd()){
        Article article;
        temp.read((char *) &article, sizeof(Article));
        Article *s = new Article();
        s = (Article*) &article;
        qDebug() << s->articleName;
     }
    temp.close();

Gdy używam QDataStream wszystko działało dobrze lecz rozmiar rekordu w pliku był różny(QDataStream dodaje długość QString przed zmianną) a ja chciałbym stały bo chce wykonywać operacje na danym pliku.

Proszę o pomoc.

4

Tak na szybko:
Problemem jest to, że masz w strukturze obiekty typu QString. Zapis i odczyt struktury działa wtedy, i tylko wtedy*, jeśli struktura składa się jedynie z POD (Plain Old Data), czyli jedynie typów prostych (przy czym bez wskaźników!).

    • oraz plik jest odczytywany na tej samej "platformie" (co w tym wypadku znaczy kilka różnych rzeczy: m.in. w grę wchodzi endiannes, kodowanie, wielkości zmiennych czy pomysł kompilatora na to jak wyalignować strukturę; na wszystko z tego są pewne work-aroundy, ale w praktyce w produkcyjnym kodzie raczej unika się wczytywania "na raz" całej struktury, i wczytuje poszczególne pola "ręcznie" je dekodując).

W Twoim wypadku musiałbyś zamienić QString na char asdf[jakaś sensowna wielkość] - wtedy wszystko będzie działać OK.
(Warto przy inicjacji wyzerować tą strukturę btw, tak żeby żadne "śmieci" z pamięci nie zostały zapisane na dysku.)

Alternatywnie musisz "zserializować" QString przy zapisie, ale ostatecznie sprowadza się to prawie (słowo klucz ;>) do tego samego co to wyżej.

Dodam, że program "trochę działa" przy odczytaniu w danych w tym samym procesie, ponieważ wewnętrzne wartości wskaźników używanych w obiekcie QString się zgadzają - ale to jest złudne działania i nie powinno się na tym polegać. Oczywiście adresy pointerów ulegają zmianie po ponownym uruchomieniu (patrz ASLR) lub, w Twoim wypadku, nie ma tych stringów w pamięci w ogóle, więc adresu (wartości wskaźników) wczytane z pliku są w ogóle nieprawidłowe.
Uproszczona zasada brzmi: "nigdy nie zapisuj wskaźników do pliku" oraz "nigdy nie zapisuj obiektów do pliku" (są wyjątki, ale są one bardzo bardzo bardzo rzadkie ;>)

0

Nie wiem czemu na to nie wpadłem wcześniej -.-.

Wielkie dzięki, pomogło, działa.

0

Ja jeszcze dodam, że nawet jeśli te dany były POD (Plain Old Data) to zakładając, że kod miałby być wielkoformatowy (taki jest zamysł Qt), to i tak byłoby źle. Polecam poczytać dokumentację QDataStream.

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