Zapis danych do plików binarnych

Odczyt danych z plików binarnych przebiega trochę inaczej niż w przypadku plików tekstowych. Dane w tych plikach muszą być zapisywane w ściśle określonym porządku. Używamy ich głównie w celu zapisywania i odczytywania struktur. Załóżmy, że tworzymy prosty program "Książka telefoniczna" i potrzebne nam są dane abonenta, np. imię, nazwisko, wiek i oczywiście numer telefonu. W tym celu tworzymy strukturę:

Unit1.cpp

struct DaneAbonenta
{
   char Nazwisko[30];
   char Imie[15];
   int  Wiek;
   int  Nr;
};

Zapis takiej struktury do pliku przebiega następująco:

Unit1.cpp

#include <fstream.h>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   //utworzenie obiektu outfile klasy ofstream
   ofstream outfile("c:\\katalog\\ksiazka.dat", ios::binary);
   //jeżeli tworzenie obiektu zakończy się niepowodzeniem to przerwij operację
   if (!outfile) return;
   //tworzenie struktury i wypełnianie jej danymi
   DaneAbonenta dane = {"Jan III", "Sobieski", 17, 72772};
   //zapis struktury
   outfile.write((char*)&dane, sizeof(dane));
   //zamknięcie pliku
   outfile.close();
}

Wyjaśnienia wymaga utworzenie struktury. Jest ona tu od razu wypełniana danymi, co w programie tego typu raczej nigdy nie ma miejsca. Można by umieścić na formularzu 4 pola Edit, do których by były wpisywane dane, a dopiero póżniej wpisane do struktury:

Unit1.cpp

dane.Wiek = Edit1->Text.ToInt();

Przy zapisywaniu pliku metodą write() klasy outfile musimy jako pierwszy argument podać wskaźnik na łańcuch (char*) stąd takie rzutowanie: (char*)&dane.

Odczyt danych z plików binarnych

Jeżeli już mamy utworzony plik z danymi, np. za pomocą omówionego wcześniej sposobu to jego odczyt jest wprost banalny:

Unit1.cpp

struct DaneAbonenta
{
   char Nazwisko[30];
   char Imie[15];
   int  Wiek;
   int  Nr;
};

#include <fstream.h>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
   //utworzenie obiektu infile klasy ifstream
   ifstream infile("c:\\katalog\\ksiazka.dat", ios::binary);
   //jeżeli tworzenie obiektu zakończy się niepowodzeniem to przerwij operację
   if (!infile) return;
   //tworzenie struktury i wypełnianie jej danymi
   DaneAbonenta dane;
   //odczyt struktury
   infile.read((char*)&dane, sizeof(dane));
   //zamknięcie pliku
   outfile.close();
   //wyświetlenie danych na etykietach Label
   
Label1->Caption = dane.Nazwisko;
    Label2->Caption = dane.Imie;
    Label3->Caption = dane.Wiek;
    Label4->Caption = dane.Nr;
}

Odczyt i zapis danych z/do plików binarnych

Za pomocą klasy fstream możemy równocześnie realizować odczyt i zapis danych. Otworzenie pliku do odczytu i zapisu wykonuje się tak:

Unit1.cpp

fstream iofile("c:\\katalog\\ksiazka.dat", ios::binary | ios::in | ios::out);

Wskaźnik plikowy

Wskaźnik plikowy jest liczbą określającą miejsce pliku, do którego dane zostaną zapisane, lub z którego zostaną one odczytane. Po otwarciu pliku ma on wartość 0, po odczytaniu (zapisaniu) 10 bajtów ma wartość 10, po odczytaniu (zapisaniu) 100 bajtów ma wartość 100, itd.

Po odczytaniu (zapisaniu) danych automatycznie zmienia on swoją wartość jednak możemy ją jawnie zmienić lub odczytać. Służą do tego następujące metody: seekg() i tellg() (klasa ifstream) oraz seekp() i tellp() (klasa ofstream).

Odczyt i zapis niesformatowanych bloków danych realizowany jest za pomocą metod get() i put().

Swobodny dostęp do pliku

Na przykładzie naszego programu "Książka telefoniczna" wytłumaczę swobodny dostęp do pliku. Załóżmy, że mamy 100 osób w bazie danych (czyli np. w pliku) i trzeba nam pobrać dane 99 osoby. Oczywiście można by odczytać po kolei 99 osób i znaleźlibyśmy interesujące nas dane. Jednak nie ma to żadnego sensu, gdyż odczytujemy niepotrzebnie dane 98 osób !

Zamiast tego możemy obliczyć wartość wskaźnika wskazującego w pliku na 99 osobę (wartość wskaźnika wskazującego na 1 osobę wynosi 0, stąd na 99 osobę mnożymy przez 98, a nie przez 99):

Unit1.cpp

int pos = 98 * sizeof(DaneAbonenta);

a następnie przesuwamy wskaźnik na obliczoną pozycję:

Unit1.cpp

infile.seekg(pos); //przy odczycie z pliku


Unit1.cpp

outfile.seekp(pos); //przy zapisie do pliku

i za pomocą read() lub write() odczytujemy/zapisujemy dane z/do tego rekordu.