[Delphi]Operacje na plikach z poziomu aplikacji

0wn3r

Były Moderator
Dołączył
Marzec 10, 2007
Posty
1330
Operacje na plikach z poziomu aplikacji

W tym artykule będą wyjaśnione operacje na plikach z poziomu aplikacji. Czym jest plik? Plik jest to zorganizowany zbiór danych przechowywany na dysku twardym, płytach CD/DVD, pendrive, itd. Operujemy na plikach dosyć często przy obsłudze/tworzeniu baz danych, jednakże zastosowań jest o wiele więcej(np. pliki konfiguracyjne, tymczasowe - przechowujące jakieś informacje na dany moment).
Czym jest atrybut danego pliku? Każdy plik ma swoją listę atrybutów(cech). Przykładem jest atrybut "Tylko do odczytu"(ang. Read-Only), gdy ten atrybut jest aktywny - większość aplikacji nie ma wpływu na dany plik(może go odczytać, ale rzadko kiedy modyfikować). Atrybuty dotyczące danego pliku są przechowywane w bajcie(jeden atrybut odpowiada jednemu bitowi, cała informacja o atrybutach pliku jest zapisywana na czterech bitach). Przykłady atrybutów w Delphi:

  • Plik tylko do odczytu - faReadOnly
  • Plik ukryty - faHidden
  • Plik systemowy - faSysFile
Jest jeszcze jeden atrybut - faArchive - jest on powiązany z tworzeniem kopii zapasowych. Gdy tworzy się nowy plik, wszystkie atrybuty są wyłączone(prócz faArchive). Aby dokonać zmian na atrybutach pliku, użyjemy funkcji FileGetAttr oraz FileSetAttr. Teraz możesz zadać sobie pytanie? Po co mi te cechy pliku? Co by było np. gdybyś miał potrzebę zmodyfikować jakiś plik, który jest "Tylko do odczytu"?
Najłatwiejszym sposobem jest po prostu wyłączyć ten atrybut, zmienić to co chcemy i przywrócić atrybut spowrotem. Wracając do tego co wcześniej mówiłem - informacje o cechach są przechowywane w bajcie. Jak wszyscy wiemy(no może nie wszyscy), bajt = 8 bitów.
  • Bit 0 - bit dla atrybutu "Tylko do odczytu"
  • Bit 1 - bit dla atrybutu "Plik ukryty"
  • Bit 2 - bit dla atrybutu "Plik systemowy"
  • Bit 3 - bit dla atrybutu "Volume ID"
  • Bit 4 - bit dla atrybutu "Podkatalogi"
  • Bit 5 - bit dla atrybutu "Archive"(to co mówiłem wcześniej - kopie zapasowe - w każdym pliku jest włączony na starcie)
  • Bit 6 & 7 - bity te są nie używane
Teraz przyjmijmy, że chcielibyśmy włączyć atrybut "Plik ukryty". Pierwsze co przychodzi na myśl to użycie funkcji której podałem wcześniej FileSetAttr i ustawić faHidden. faHidden - ma wartość 00000002 binarnie, oraz 02 heksadecymalnie. Jednakże ustawiając tryb ukryty - skasowalibyśmy atrybut odpowiadający za tworzenie kopii zapasowych(ustawiając jeden atrybut wyzerowalibyśmy pozostałe). System binarny i heksadecymalny(tzw. szesnastkowy) to systemy podobne do dziesiętnego(tego, którego używamy na co dzień). W systemie dziesiętnym mamy liczby od 0 do 9, natomiast w systemie binarnym 0 oraz 1. W systemie szesnastkowym posługujemy się liczbami od 0 do F, tj. 0...9, A, B, C, D, E, F. System szesnastkowy pozwala na łatwą zamianę liczby z systemu binarnego. Napisałem wcześniej, że bajt ma osiem bitów. Przykładową liczbą niech będzie 188, w systemie binarnym zapiszemy ją 10111100, w szesnastkowym jako BC. Jeśli chcielibyśmy zapisać w naszym kodzie liczbę w systemie szesnastkowym, poprzedzamy ją znakiem "$"(dolara), czyli $BC. Patrząc na wartość binarną naszego BC, tj. 10111100 - można zauważyć, które atrybuty są aktywne poprzez czytanie od prawej do lewej strony. Tak więc aktywny jest atrybut "Archive", "Podkatalogi", "Volume ID", "Plik systemowy", "Tylko do odczytu". Teraz gdybyśmy chcieli ustawić atrybut "Plik ukryty" użylibyśmy funkcji FileSetAttr, włączając atrybut "Plik ukryty", jednakże zerując pozostałe. Wprowadzamy nową wartość, a poprzednia jest kasowana. A więc, jeśli chcielibyśmy ustawić dwa atrybuty na raz, piszemy tak:
Kod:
FileSetAttr('plik.txt', faHidden+faArchive);
Wtedy włączony jest i faHidden & faArchive. Hm no dobrze, ale co jeśli mamy ochotę tak jak mówiłem wcześniej dołączyć/wyłączyć jakiś atrybut? Rozwiązanie:
Kod:
var
 atrybut : Integer; // 1
begin
 atrybut := FileGetAttr('plik.txt'); // 2
 atrybut := atrybut OR faHidden; // 3
 FileSetAttr('plik.txt', atrybut); // 4
end;
  1. Deklaracja zmiennej "atrybut" typu integer.
  2. Pobieranie cech pliku do wyżej zadeklarowanej zmiennej za pomocą FileGetAttr();
  3. Użycie operatora 'OR' w celu dopisania nowego atrybutu.
  4. Przypisanie plikowi nowego atrybutu za pomocą FileSetAttr();
Banalne, czyż nie? Skoro już trochę wiecie o cechach pliku, można by stworzyć jakiś program. Odpalamy Borland Delphi! Dodajemy komponenty:
  • Button1 - ustawiamy Caption(opis) na "Zapisz"
  • Button2 - ustawiamy Caption(opis) na "OK"
  • Button3 - ustawiamy Caption(opis) na "Wyjdź"
  • Edit - kasujemy wszystko co jest w Text
  • FileListBox - FileEdit ustaw na Edit1
  • DirectoryListBox - FileList ustaw na FileListBox1
  • DriveComboBox - DirList ustaw na DirectoryListBox1
  • CheckBox - opis "Tylko do odczytu"
  • CheckBox - opis "Plik ukryty"
  • CheckBox - opis "Plik systemowy"
  • CheckBox - opis "Archive"
Układamy sobie to wszystko mniej więcej tak jak ja:

------------------------
Pod:
Kod:
"implementation

{$R *.dfm}"
Piszemy:
Kod:
var
 nazwa : String;
 atrybut : Integer;
------------------------
Obsługa kliknięcia przycisku "Zapisz":
Kod:
procedure TForm1.Button1Click(Sender: TObject); // PRZYCISK ZAPISZ
begin
atrybut := 0;
if CheckBox1.Checked = True then
  atrybut := atrybut OR faReadOnly;
if CheckBox2.Checked = True then
  atrybut := atrybut OR faHidden;
if CheckBox3.Checked = True then
  atrybut := atrybut OR faSysFile;
if CheckBox4.Checked = True then
  atrybut := atrybut OR faArchive;
FileSetAttr(nazwa, atrybut);
end;
Obsługa kliknięcia przycisku "OK":
Kod:
procedure TForm1.Button2Click(Sender: TObject);
begin
  nazwa := Edit1.Text;
  atrybut := FileGetAttr(nazwa);
  if atrybut and faReadOnly = faReadOnly then
    CheckBox1.Checked := True;
  else
    CheckBox1.Checked := False;
  if atrybut and faHidden = faHidden then
    CheckBox2.Checked := True;
  else
    CheckBox2.Checked := False;
  if atrybut and faSysFile = faSysFile then
    CheckBox3.Checked := True;
  else
    CheckBox3.Checked := False;
  if atrybut and faArchive = faArchive then
    CheckBox4.Checked := True;
  else
    CheckBox4.Checked := False;
end;
Obsługa kliknięcia przycisku "Wyjdź":
Kod:
procedure TForm1.Button3Click(Sender: TObject);
begin
  Application.Terminate;
end;

Tym bym zakończył pierwszą część artykułu. Myślę, że kod jest zrozumiały. Kolejne części ukażą się za parę dni. Mam nadzieje, że to się komuś przyda.

Pozdrawiam.
 

0wn3r

Były Moderator
Dołączył
Marzec 10, 2007
Posty
1330
Pliki tekstowe
Jak wiemy - istnieją dwa rodzaje plików - tekstowe oraz binarne. Oczywiście wszystkie pliki należą do któryś z tych dwóch kategorii(jest wiele różnych sposobów przechowywania i formatowania danych zapisanych w pliku). Otóż większość z nas powinna wiedzieć czym jest plik tekstowy. Plik tekstowy składa się ze zbioru znaków ASCII. Każdy wiersz w pliku tekstowym kończy się znakiem CR/LF. Powiedzmy, że teraz chcielibyśmy zacząć wykonywać jakieś operacje na plikach tekstowych. Pierwsze co musimy, to stworzyć zmienną plikówą(za jej pomocą odwołujemy się do naszego pliku):
Kod:
var
  Plik: TextFile;
No skoro jest już zmienna - trzeba wskazać jej jakiś plik. Do takiej operacji służy procedura AssignFile. Przykład użycia:
Kod:
AssignFile(Plik, 'plik.txt');
Proste? Proste. Gdy już zwiążemy jakiś plik ze zmienną typu TextFile - odwołujemy się do pliku tylko i wyłącznie używając naszej zmiennej(w tym przypadku 'Plik'). Istnieje parę procedur, które dotyczą obsługę plików. Oto one:
  • Rewrite - tworzy/otwiera/nadpisuje plik
  • Writeln - wpisuje do pliku wiersz(który kończy się znakiem o którym wcześniej wspominałem - CR/LF)
  • CloseFile - zapisuje zmiany i zamyka plik
  • Reset - otwiera istniejący plik
  • Readln - odczytuje jakiś wiersz znajdujący się w pliku
No to świetnie. Wszystko już wiemy. Teraz czas napisać jakiś program! Tak jak w poprzedniej części - odpalamy Borland Delphi! Wrzucamy na formę:
  • Button - opis "Zapisz"
  • Button - opis "Wczytaj"
  • Button - opis "Wyjdź"
  • 2x Edit
Ja sobie to tak ułożyłem:

----------------------
Obsługa kliknięcia przycisku "Zapisz":
Kod:
procedure TForm1.Button1Click(Sender: TObject);
var
  PlikWyjsciowy : TextFile; // 1
  nazwapliku, zapis : String;
begin
  nazwapliku := 'plik.txt'; // 2
  AssignFile(PlikWyjsciowy, nazwapliku); // 3
  Rewrite(PlikWyjsciowy); // 4
  zapis := Edit1.Text; // 5
  Writeln(PlikWyjsciowy, zapis); // 6
  CloseFile(PlikWyjsciowy); // 7
end;
  1. Deklaracja zmiennej typu TextFile
  2. Nazwanie naszego pliku jako plik.txt
  3. Związanie zmiennej typu TextFile z plikiem
  4. Tworzenie pliku
  5. Pobranie tekstu z Edit1 do zmiennej zapis
  6. Wpisanie pobranego tekstu do naszego pliku
  7. Zamknięcie pliku
Obsługa kliknięcia przycisku "Wczytaj":
Kod:
procedure TForm1.Button2Click(Sender: TObject);
var
  PlikWejsciowy : TextFile;
  nazwapliku, wczytaj : String;
begin
  nazwapliku := 'plik.txt';
  AssignFile(PlikWejsciowy, nazwapliku);
  Reset(PlikWejsciowy); // 1
  Readln(PlikWejsciowy, wczytaj); // 2
  Edit2.Text := wczytaj; // 3
  CloseFile(PlikWejsciowy);
end;
  1. Otwarcie pliku
  2. Pobranie zapisanych w nim znaków
  3. Wyświetlenie tekstu w Edit2
Obsługa kliknięcia przycisku "Wyjdź":
Kod:
procedure TForm1.Button3Click(Sender: TObject);
begin
  Application.Terminate;
end;
Tak więc nasza aplikacja potrafi odczytywać pojedyncze wiersze z pliku oraz je zapisywać. Co jeśli chcielibyśmy odczytać cały plik składający się z x wierszy? Aby poprawnie odczytać pliki składające się z x wierszy - trzeba wiedzieć gdzie jest koniec pliku tekstowego - sprawdzamy to za pomocą funkcji EoF - czyli end of file. Możecie sobie poeksperymentować. Jak wspomniałem na początku - drugim rodzajem plików są pliki binarne - nie są one przeznaczone do oglądania i zawierają binarne informacje, które nie są dla wszystkich zrozumiałe(głównie dla aplikacji). Jedną z odmian plików binarnych są pliki zdefiniowane oraz(no zgadnij) - pliki nie zdefiniowane. O plikach zdefiniowanych i nie zdefiniowanych napiszę w kolejnej części.
 

0wn3r

Były Moderator
Dołączył
Marzec 10, 2007
Posty
1330
Pliki binarne
W poprzedniej części pisałem o plikach tekstowych. W tej części zajmiemy się plikami binarnymi, tak jak napisałem na końcu ostatniego artykułu. Otóż pierwszą odmianą plików binarnych, są pliki binarne zdefiniowane. Przykładem jakiegoś programu, który wykorzystuje pliki zdefiniowane może być program, który pełni funkcję np. książki adresowej, telefonicznej. Taki program możemy stworzyć, jeśli zaprojektujemy własną strukturę elementów, które zapiszemy do pliku. I tak też zrobimy. Stwórz nowy projekt i do sekcji implementation wstaw:
Kod:
Info = record
 Imie: String[15];
 Nazwisko: String[20];
 Telefon: String[15];
 Miasto: String[30];
 Kodpocztowy: String[10];
No i w ten sposób stworzyliśmy własny typ danych o nazwie "Info". Ten nasz typ nazywany jest rekordem. Wszystko to powinno wyglądać tak:
Kod:
implementation

{$R *.dfm}

type

Info = record // nasz typ, który zawiera same zmienne typu String, czyli po prostu łańcuchy tekstowe
 Imie: String[15];
 Nazwisko: String[20];
 Telefon: String[15];
 Miasto: String[30];
 Kodpocztowy: String[10];
Okej, typ stworzony. Teraz deklarujemy zmienne:
Kod:
var
 Adres: File of Info;
 Dane: Info;
 nazwa: String;
 aktual: Longint;
Zmienna "Adres" jest zmienną plikową. Natomiast zmienna "Dane" będzie pełniła funkcję bufora, dzięki któremu będziemy w stanie komunikować się z plikiem. Aby kontynuować tworzenie naszego programu musimy zapoznać się z paroma procedurami, funkcjami, które mogą okazać się nam niezbędne.
  • Procedura AssignFile() - służy do związania zmiennej plikowej z danym nam plikiem

  • Procedura Reset() - otwiera istniejący plik, pod warunkiem, że związaliśmy z nim już naszą zmienną plikową

  • Procedura Rewrite() - tworzy oraz otwiera plik(oczywiście po związaniu z plikiem naszej zmiennej plikowej)

  • Procedura Read() - odczytuje element z pliku, który identyfikujemy posługując się zmienną plikową

  • Procedura Write() - zapisuje element do pliku, który identyfikujemy posługując się zmienną plikową

  • Procedura CloseFile() - zamyka plik, który jest identyfikowany przez zmienną plikową, zapisując uprzednio wszelkie wprowadzone zmiany

  • Funkcja Eof - czyli end of file, zwraca wartość True(czyli 1), jeśli natkniemy się na koniec pliku
No to zapoznanie mamy już za sobą. Tak więc stworzyliśmy własny typ, zadeklarowaliśmy niezbędne zmienne. Myślę, że nadszedł czas na przykład! Dodajemy na formę następujące komponenty:

  • Button - opis "Poprzedni"
  • Button - opis "Następny"
  • Button - opis "Zapisz"
  • Button - opis "Stwórz nowy"
  • Button - opis "Zakończ"
  • 5x Edit
Układamy to sobie wszystko, żeby jakoś wyglądało:


Tym razem nie będę tłumaczyć wszystkiego w tym poście, gdyż byłoby to lekko problematyczne, dlatego daję link do całego projektu, w środku są komentarze objaśniające wszystko co się dzieje w programie.

POBIERZ KOD ŹRÓDŁOWY

Następna część wkrótce. Jeśli by ktoś chciał abym coś objaśnił to proszę pisać prywatne wiadomości.

Pozdrawiam.
 
Do góry Bottom