pecet jogger

cokolwiek o czymkolwiek

C++, nie całkiem trywialny sposób zapisu danych w formie binarnej

28/08/2007, 12:48:46 | 4 komentarze | Programowanie, Techblog

Ostatnio jakoś, kończąc mój pewien program, chciałem zapisać dane w pliku w formie binarnej. Jakoś mimo że bawię się C++ już od paru lat, nigdy mi nie potrzebna taka opcja była, a jak już to korzystałem z instrukcji put oraz get. Będąc zmuszony pisać w czystym ANSI-C, korzystałem natomiast z dość ciekawych funkcji fread oraz fwrite, które w bardzo prosty sposób pozwalają zapisać dowolną strukturę, w formacie binarnych, w pliku oraz równie prosto ją odczytać, np. (przyjmujemy że hs jest jakąś strukturą, a highscorelist 5 elementową tablicą o typie tej struktury)
FILE *plik;
plik = fopen("highscores.dat", "w");
fwrite(highscorelist, sizeof(struct hs), 5, plik);
fclose(plik);
oraz
int a;
FILE *plik;
plik = fopen("highscores.dat", "r");
if(plik != NULL) /* jeśli plik istnieje i prawidlowo sie otwarł */
{
     fread(highscorelist, sizeof(struct hs), 5, plik);
     fclose(plik);
}

Chciałem jednak skorzystać z "czysto ceplusplusowych" konstrukcji. Standardowe operatory << oraz >> z klasy fstream, odpadały ponieważ zapisywały one dane na wyjście w sposób tekstowy - sformatowany. Zacząłem coś kombinować z instrukcjami write oraz read. Niestety z nie wiadomych, mi, przyczyn obie funkcje używały do zapisu oraz odczytu typu char, a dokładniej wskaźnika na tablicę z tym typem oraz długością. Próbowałem więc zwykłego rzutowania zmiennej int na zmienną char *, bezskutecznie. Na szczęście korzytając z dobrodziejstw pewnej wyszukiwarki, po jakimś czasie, udało mi się znaleźć rozwiązanie. Tak więc należało użyć zamiast "zwykłego" rzutowania operator reinterpret_cast, specjalnie stworzony do konwertowania niekompatybilnych wskaźników... Korzystając więc z wiedzy przedstawionej w tamtym dokumencie zapis w postaci binarnej, dowolnej zmiennej, w tym przypadku int, wygląda następująco:
plik.write(reinterpret_cast(&naszint), sizeof(naszint)); natomiast odczyt, jak można łatwo wywnioskować
char tmp[4];
plik1.read(tmp, 4);
int naszint = reinterpret_cast(tmp);
Zasadniczo obie czwórki (4) można by zastąpić, i jest to wskazane, siezof(int) lub sizeof(naszint), czego tutaj nie zrobiłem aby sprawy nie komplikować.

MiB (#) 28/08/2007 - 15:06:33

Witam!

Nie wiem, czy dobrze zrozumiałem problem, ale jego rozwiązaniem może być dodanie flagi "ios::binary" podczas otwierania pliku (a przynajmniej załatwiało toto problem na zajęciach, kiedy trzeba było operować na plikach z zawartością binarną ;) ).

Więcej informacji: http://www.cplusplus.com/reference/iostream/fstream/fstream.html

pecet (#) 28/08/2007 - 15:07:29

Dodawałem flagę binary, i bez tego triku i tak nic nie zdziałałem ;)

Khorne (#) 28/08/2007 - 18:08:12

Flaga ios::binary oznacza tylko niezamienianie znaków końca linii na niektórych systemach.

W zakresie zapisu danych binarnych polecam także konstrukcję:

ofstream plik;
vector<char> buffer;
copy(buffer.begin(), buffer.end(), streambuf_iterator<char>(plik.rdbuf()));

mh (#) 28/08/2007 - 20:21:22

A do ladnego opakowania danych polecam Boost.Serialization.

Dodaj komentarz