Pliki typowane

Adam Boduch

Zanim przeczytasz ten artykuł powinieneś najpierw poczytać artykuł "Pliki", który opisuje podstawowe operacje dotyczące plików.

Normalnie zapisując jakieś dane do pliku zapisujemy jakiś ciąg znaków. Pliki typowane natomiast nazywane są często plikami rekordowymi ponieważ służą do zapisywania rekordów. Przykładowo możesz zapisać taki oto rekord:

  TVideoRec = packed record
    NazwaFilmu : String[30];
    Numerek : WORD;
    Data : TDateTime;
  end;

Teraz gdy mamy już odpowiedni rekord to należy do niego utworzyć typ pliku:

TVideoFile = file of TVideoRec;
W taki sposób możemy stworzyć nasz unikalny, niepowtarzalny system wymiany danych :) Tak, napiszemy teraz przykładową aplikację, która gromadzić będzie informacje o wypożyczonym filmie, a następnie dane te będzie zapisywać do pliku. Tak więc w jednym pliku będziemy mieli dane, które będzie można później odczytać.

Zapisywanie danych do plików typowanych odbywa się tak samo jak w przypadku zwykłych plików tekstowych. Nie istnieje natomiast procedurę "Append", która jak zapewne pamiętasz ustawiała pozycje do zapisu bezpośrednio na końcu pliku. Taki coś w plikach typowanych nie występuje.

Nowe polecenia w plikach typowanych to: FileSize. To polecenie pobiera ilość danych znajdujących się w pliku. W naszym przypadku ilość rekordów wpisanych do pliku. Kolejne bardzo przydatne polecenie to Seek. Ustawia ono plik na wybranej pozycji której dotyczyć będzie kolejna operacja. Czyli np:

Seek(F, 2);

Ustawia pozycje na drugim rekordzie ( w naszym wypadku ).

Jeszcze jedno polecenie to FilePos. Podaje ono pozycje na której teraz następuje operacja.

Nie martw się jeżeli jeszcze wszystkiego nie rozumiesz. Pozostałe polecenie są takie same jak w przypadku zwykłych plików.

Piszemy program...

Przyszedł czas, aby napisać program, który korzystał będzie z plików typowanych. Jakie komponenty będą nam potrzebne? Umieść na formie komponent typu "TComboBox" oraz 2 kontrolki typu "TEdit", "TDateTimePicker" oraz "TButton".

Na początek przyjmiemy, że plik, do którego będą zapisywane dane będzie się nazywał "Dane.dat":

const
  FileName = 'Data.dat'; // tutaj przechowywane beda informacje...

Na samy początek potrzebna nam będzie procedura odczytująca ilość rekordów zawartych w pliku. Oto ona - ilość danych będzie dodawany do kontrolki "cmbRec" [ TComboBox ]:

procedure TMainForm.RefreshRec;
var
  VFile : TVideoFile;
  I : Integer;
begin
{
  Ta procedura ma za zadanie odswiezyc liste rekordow znajdujacych
  sie w danym pliku. Zawartosc komponentu najpierw zostaje czyszczona,
  a pozniej nastepuje odczytanie wszystkich rekordow i dodanie ich do listy.
}
  AssignFile(VFile, FileName);
  if not FileExists(FileName) then // Jezeli plik nie istnieje - nie rob nic
    Exit else
    
  Reset(VFile);
  cmbRec.Items.Clear; // czysc liste danych w komponencie

{
  Ponizsza petla ma za zadanie dodawanie kolejnych pozycji do listy.
  "FileSize" zawiera informacje o ilosci rekordow znajdujacych sie
  w pliku.
}

  For I := 0 to FileSize(VFile) -1 do
    cmbRec.Items.Add('Rekord nr: ' + IntToStr(i));

  CloseFile(VFile);
end;

Zauważ, że tutaj znalazło zastosowanie użycie polecenia FileSize. Ale zacznijmy od początku. Jako zmienne zadeklarowane zostały dwie - pierwsza wskazuje na nowy typ pliku [ zadeklarowaliśmy ją na początku ]. Pozostałe polecenia są takie same jak w przypadku plików tekstowych. Dochodzimy do pętli, która wykonywana zostaje tyle razy ile jest rekordów za każdym razem dodając numer tegoż rekordu do listy.

To już mamy - teraz najważniejsze, czyli sama procedura zapisująca rekord do pliku:

procedure TMainForm.WriteDate;
var
  VFile : TVideoFile; // zmienna wskazuje na nowy typ plikow
  VRec : TVideoRec; // zmianna wskazuje na rekord
  FSize : Integer; // ilosc danych znajdujacych sie w pliku...
begin
  AssignFile(VFile, FileName); // skojarz nazwe pliku ze zmienna...
  if not FileExists(FileName) then // jezeli plik nie istnieje...
    Rewrite(VFile) else //... stworz go, a jezeli istnieje...
  Reset(VFile); //... tylko otworz

  try
    FSize := FileSize(VFile); // pobierz rozmiar ( ilosc danych )
    if FSize > 0 then  // Czy plik nie jest pusty?
      Seek(VFile, FSize); // ustaw na samym koncu pliku

{
  Dodaj do rekordu dane wziete z kontrolek tekstowych znajdujacych
  sie na formie.
}
    with VRec do
    begin
      NazwaFilmu := edtFname.Text;
      Numerek := StrToInt(edtNumber.Text);
      Data := Date.DateTime;
    end;
    Write(VFile, VRec); // zapisz rekord do pliku
  finally
    CloseFile(VFile); // zamknij plik
    RefreshRec; // wywolaj procedure ( patrz: linia 140 )
  end;
end;

Na samym początku sprawdzane jest, czy plik został utworzony, czy też nie. Jeżeli nie to go tworzy. Później zmienna "FSize" przechowuje liczbę rekordów znajdujących się w pliku. Kolejno następuje ustawienie się na samym końcu pliku (polecenie Seek). Następnie wypełnienie rekordu danymi znajdującymi się w kontrolkach no i w końcu zapisanie całego rekordu na samym końcu. Zauważ, że przy końcu procedury następuje wywołanie innej - "RefreshRec", która odczytuje ilość rekordów ( teraz zapisaliśmy nowy więc ta liczba się zwiększyła ).

Na samym końcu procedura odczytująca rekord. Procedura ta zawierać będzie jeden prarametr - w nim będzie informacja o tym, który rekord chcemy odczytać:

procedure TMainForm.ReadData(NumberOfRec: Byte);
var
  VFile : TVideoFile;
  VRec : TVideoRec;
begin
{
  Ta procedura odczytuje rekord. Jako parametr tej procedury podawany
  jest numer rekordu, ktory ma byc odczytany.
}
  AssignFile(VFile, FileName);
  Reset(VFile);

  try
    Seek(VFile, NumberOfRec); // ustaw na danym rekordzie....
    Read(VFile, VRec); // odczytaj rekord
{ Teraz dane znajduja sie w rekordzie i nalezy je przypisac do kontrolek }
    with VRec do
    begin
      edtFName.Text := NazwaFilmu;
      edtNumber.Text := IntToStr(Numerek);
      Date.DateTime := Data;
    end;
  finally
    CloseFile(VFile); // zamknij plik
  end;
end;

W tym wypadku polecenie Seek ustawia na pozycji której numer odpowiada numerowi zawartemu w zmiennej "NumberOfRec".

Jeżeli czegoś nie rozumiesz to pisz, a tutaj masz cały kod programu:

(****************************************************************)
(*                                                              *)
(*    Przykladowy program korzystajacy z plikow typowanych      *)
(*           Copyright (c) 2001 by Adam Boduch                  *)
(*        All rights reserved by Service for programmers        *)
(*             HTTP://WWW.PROGRAMOWANIE.OF.PL                   *)
(*           E - mail:  boduch@poland.com                       *)
(*  Build: 01.04.2001 r.                                        *)
(*                                                              *)
(****************************************************************)

unit MainFrm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, StdCtrls;

type
  TMainForm = class(TForm)
    grbInfo: TGroupBox;
    lblName: TLabel;
    edtFName: TEdit;
    lblNumber: TLabel;
    edtNumber: TEdit;
    lblDate: TLabel;
    Date: TDateTimePicker;
    btnSave: TButton;
    cmbRec: TComboBox;
    procedure cmbRecChange(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    procedure ReadData(NumberOfRec : Byte);
    procedure WriteDate;
    procedure RefreshRec;
  end;

{
  Ponizszy rekord przechowuje informacje o kliencie. Zapisywany on
  bedzie do pliku...
}
  TVideoRec = packed record
    NazwaFilmu : String[30]; // nazwa filmu
    Numerek : WORD; // numer klienta
    Data : TDateTime; // data wypozyczenia
  end;

  TVideoFile = file of TVideoRec; // wskazanie na rekord [ nowy typ ]

var
  MainForm: TMainForm;

implementation

const
  FileName = 'Data.dat'; // tutaj przechowywane beda informacje...

{$R *.DFM}

procedure TMainForm.WriteDate;
var
  VFile : TVideoFile; // zmienna wskazuje na nowy typ plikow
  VRec : TVideoRec; // zmianna wskazuje na rekord
  FSize : Integer; // ilosc danych znajdujacych sie w pliku...
begin
  AssignFile(VFile, FileName); // skojarz nazwe pliku ze zmienna...
  if not FileExists(FileName) then // jezeli plik nie istnieje...
    Rewrite(VFile) else //... stworz go, a jezeli istnieje...
  Reset(VFile); //... tylko otworz

  try
    FSize := FileSize(VFile); // pobierz rozmiar ( ilosc danych )
    if FSize > 0 then  // Czy plik nie jest pusty?
      Seek(VFile, FSize); // ustaw na samym koncu pliku

{
  Dodaj do rekordu dane wziete z kontrolek tekstowych znajdujacych
  sie na formie.
}
    with VRec do
    begin
      NazwaFilmu := edtFname.Text;
      Numerek := StrToInt(edtNumber.Text);
      Data := Date.DateTime;
    end;
    Write(VFile, VRec); // zapisz rekord do pliku
  finally
    CloseFile(VFile); // zamknij plik
    RefreshRec; // wywolaj procedure ( patrz: linia 140 )
  end;
end;

procedure TMainForm.ReadData(NumberOfRec: Byte);
var
  VFile : TVideoFile;
  VRec : TVideoRec;
begin
{
  Ta procedura odczytuje rekord. Jako parametr tej procedury podawany
  jest numer rekordu, ktory ma byc odczytany.
}
  AssignFile(VFile, FileName);
  Reset(VFile);

  try
    Seek(VFile, NumberOfRec); // ustaw na danym rekordzie....
    Read(VFile, VRec); // odczytaj rekord
{ Teraz dane znajduja sie w rekordzie i nalezy je przypisac do kontrolek }
    with VRec do
    begin
      edtFName.Text := NazwaFilmu;
      edtNumber.Text := IntToStr(Numerek);
      Date.DateTime := Data;
    end;
  finally
    CloseFile(VFile); // zamknij plik
  end;
end;


procedure TMainForm.cmbRecChange(Sender: TObject);
begin
{
  Jezeli uzytkownik z listy dostepnych rekordow wybierze jakis
  do nastepuje wywolanie procedury, ktora ma odczytac rekord.
  Jako parametr podawany jest indeks w komponencie "cmbRec".
}
  ReadData(cmbRec.ItemIndex);
end;

procedure TMainForm.btnSaveClick(Sender: TObject);
begin
{ Po nacisnieciu przycisku wywolaj procedure }
  WriteDate;
end;

procedure TMainForm.RefreshRec;
var
  VFile : TVideoFile;
  I : Integer;
begin
{
  Ta procedura ma za zadanie odswiezyc liste rekordow znajdujacych
  sie w danym pliku. Zawartosc komponentu najpierw zostaje czyszczona,
  a pozniej nastepuje odczytanie wszystkich rekordow i dodanie ich do listy.
}
  AssignFile(VFile, FileName);
  if not FileExists(FileName) then // Jezeli plik nie istnieje - nie rob nic
    Exit else
    
  Reset(VFile);
  cmbRec.Items.Clear; // czysc liste danych w komponencie

{
  Ponizsza petla ma za zadanie dodawanie kolejnych pozycji do listy.
  "FileSize" zawiera informacje o ilosci rekordow znajdujacych sie
  w pliku.
}

  For I := 0 to FileSize(VFile) -1 do
    cmbRec.Items.Add('Rekord nr: ' + IntToStr(i));

  CloseFile(VFile);
end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  RefreshRec; // wywolaj procedure
end;

end.

Zobacz też:

7 komentarzy

Powiedzcie mi jeszcze, jak mogę edytować już istniejące rekordy.
Bo na razie próbuję zrobić tak że ustawiam plik na tym rekordzie który chcę zmienić ale zamiast tego zmienia mi się następny. Niby wystarczy cofnąć o 1 ale wtedy nie da się edytować pierwszego rekordu...

Poniewaz nie wszystkie bitmapy sa takiego samego rozmiaru.

Jakoś nie działa mi ta sztuka na zmiennych typu TBitmap. Wie ktoś może dlaczego pojawia mi sie błąd "Acces violation"?

Mam pytanie: Czy w ten sposob ładują sie wszystkie rekordy do pamieci? Czy też jest ładowany aktualnie potrzebny (przez itemindex)?

Dobre, przydatne do większości programów z obsługą plików! I bez szyfrowania :P

Artykol naprewde przydatny dla tych, ktozy chca zrobic (zalozyc) wlasny system plikow !

[quote]wlasny system plikow[/quote] rotfl