Autor Wątek: c++ Struct , zapis i odczyt z pliku  (Przeczytany 1119 razy)

Offline beermaster

  • Użytkownik

# Styczeń 28, 2017, 15:13:29
Witam, czytam tutoriale itp. ale nie działa mi to.
Chciałem zapisać struct do pliku a poźniej odczytać ale nie wiem co robię źle.
Przy zapisie , plik jest tworzony. Ale po odczytaniu nie mam tych danych co zapisałem.

Struct:
struct st_profil
{
std::string nazwa[5];
int ilosc;
};

st_profil PROFIL;

Zapis:
        std::fstream plik;
        plik.open("./SAVE/PROFIL.dat", std::ios::out | std::ios::binary);
        plik.write((char*)&PROFIL, sizeof(st_profil));
plik.close();


Odczyt:
        std::fstream plik;
plik.open("./SAVE/PROFIL.dat", std::ios::in | std::ios::binary);
        plik.read((char*)&PROFIL, sizeof(st_profil));
plik.close();

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline KeeL

  • Użytkownik

# Styczeń 28, 2017, 16:38:37
Nie zapiszesz std::string w taki sposób do pliku. std::string to tak naprawdę opakowanie, które ma wskaźnik na dane i ich długość (tak w dużym uproszczeniu). Zapisując w ten sposób zapisujesz właśnie do pliku ten wskaźnik i długość danych.
Musisz zrobić sobie funkcję, która zapisze Ci do pliku tą strukturę, a następnie ją wczyta.

Taka funkcja mogła by wyglądać przykładowo tak:
void saveToFile(st_profil p){
std::fstream plik;
        plik.open("./SAVE/PROFIL.dat", std::ios::out | std::ios::binary);
        for( auto i = 0; i < 5; ++i ){
        plik.write( p.nazwa[0].c_str() );
        }
        plik.write( p.ilosc);
  plik.close();
}
Adekwatnie wyglądała by funkcja wczytująca.
« Ostatnia zmiana: Styczeń 28, 2017, 16:42:16 wysłana przez KeeL »

Offline beermaster

  • Użytkownik

# Styczeń 28, 2017, 16:50:29
A gdyby nie było stringa a tylko dane typu int czy powiedzmy double , to można w ten sposób zapisywać i odczytywać ?

Offline KeeL

  • Użytkownik

# Styczeń 28, 2017, 18:27:51
A gdyby nie było stringa a tylko dane typu int czy powiedzmy double , to można w ten sposób zapisywać i odczytywać ?
Jeżeli będą to proste typy, żadnych tablic, to tak.

Offline MaxGarden

  • Użytkownik
    • Profil na warsztacie

# Styczeń 28, 2017, 22:52:06
Statyczne tablice (o znanym, w czasie kompilacji, rozmiarze) typów prostych jak najbardziej można zapisywać w ten sposób.

Offline .c41x

  • Użytkownik
    • homepage

# Styczeń 29, 2017, 12:34:47
Statyczne tablice (o znanym, w czasie kompilacji, rozmiarze) typów prostych jak najbardziej można zapisywać w ten sposób.
Pod warunkiem, że uważa się na padding.

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Styczeń 29, 2017, 14:38:05
Typy proste - tzw. POD - Plain Old Data, czyli np. bool, char, int, float, a także ich tablice można zapisywać i odczytywać tak, jak próbujesz, czyli jako surowe dane binarne. Wskaźników oraz obiektów różnych klas, takich jak std::vector czy std::string, ogólnie nie należy w ten sposób traktować.

Tym, jaki rozmiar mają poszczególne typy, czy zapis liczb w pamięci jest big-endian, czyli little-endian oraz jakie są zasady wyrównania typów, należy się przejmować przy przenoszeniu danych między programami skompilowanymi na różne architektury (kompilator Visual Studio kontra GCC, Windows kontra Linux, 32-bit kontra 64-bit, procesory x86 kontra ARM itd.) Jeżeli Twój program ma działać tylko na platformie jednego typu, to nie powinno być problemu.

Natomiast Twój konkretny problem najprościej byłoby rozwiązać używając w strukturze tablicy znaków zakończonej zerem, zamiast std::string.

struct st_profil
{
        nazwa[5][256];
        int ilosc;
};

st_profil PROFIL;

Wtedy można taką strukturę zapisać i odczytać z pliku tak, jak chciałeś. Są jednak z tym rozwiązaniem dodatkowe problemy:

- Operowanie na takich łańcuchach jest mniej wygodne, niż z użyciem std::string. Trzeba używać funkcji typu strcpy, strcat, strlen itd.
- Maksymalna długość łańcucha jest ograniczona i z góry określona w kodzie. Trzeba uważać, żeby jej nie przekroczyć.
- Łańcuch zapisany w pliku zawsze zajmuje tyle bajtów, ile wynosi jego maksymalny rozmiar i miejsce na niewykorzystane znaki marnuje się.

Offline voytech

  • Użytkownik

  • +1
# Styczeń 29, 2017, 22:16:31
W zasadzie to powtarzam się za Reg-iem ale co mi tam :) - skoro jest to c++ to nie warto rezygnować z std::string na rzecz tablicy znaków. W Twoim programie to pole struktury może być wykorzystywane do wielu różnych rzeczy i byłoby wygodnie móc skorzystać z metod klasy string.

Najlepiej napisać sobie dwie funkcje która zapisuje i odczytuje stringa z/do pliku znak po znaku plus na początku wielkość napisu. To jest jednorazowa robota w jednym miejscu a decyzja o zamianie stringa na tablice znaków uprzykrzy się później w paru różnych miejscach.

Odnośnie big i little-endian. Na samym początku powinieneś zadecydować który z nich będzie stosowany w pliku i się tego później trzymać. Dane typu int, short, float, double też powinny mieć funkcje ich zapisu bajt po bajcie wtedy będzie konkretne miejsce gdzie trzeba będzie zrobić ewentualną konwersję big->little lub little->big.

Jak endianness pliku jest konkretne i stałe to później łatwiej będzie zaimplementować wczytywanie danych pod inne architektury jak również w innych językach programowania.

Twoja struktura powinna mieć również metodę która będzie zapisywała poszczególne pola do pliku:
Kod: (c++) [Zaznacz]
struct st_profil {
    std::vector<std::string> nazwy(5);
    int ilosc;

    void savetofile(fstream plik);
    void loadfromfile(fstream plik);
}

void st_profil::savetofile(fstream plik) {
    for (auto s : nazwy) {
        save_string(plik, s);
    }
    save_int(plik, ilosc);
}

Wady:
- po dodaniu pola do struktury trzeba pamiętać o uaktualnieniu metody serializującej

Zalety:
- po dodaniu pola do struktury można specjalnie zapomnieć o zapisaniu jej do pliku, takie pseudo transient z javy :)

Ps. Jak już jest konkretne miejsce gdzie jest zapis struktury do pliku to nic nie stoi na przeszkodzie, żeby zrobić wczytywanie/zapisywanie XML albo JSON.