D/Obsługa plików

Z Wikibooks, biblioteki wolnych podręczników.

< D

Spis treści

[edytuj] Obsługa plików

Obsługa plików jest bardzo często wykorzystywana w programowaniu - to właśnie w plikach przechowywane są wszelkie informacje, które muszą zostać zachowane po wyłączeniu programu (konfiguracja, wyniki i inne).

W języku D do różnych operacji na plikach wykorzystujemy moduł std.file, choć w wypadku odczytu i zapisu do plików wygodniej jest posłużyć się strumieniami i modułem std.stream.

[edytuj] Sprawdzanie, czy plik istnieje

Aby sprawdzić, czy dany plik istnieje, należy się posłużyć funkcją bool exists(char[] filename) zadeklarowaną w module std.file (pamiętaj, by go wcześniej zaimportować). Jako argument podajemy nazwę pliku (o ile plik znajduje się w tym samym katalogu, co program) lub ścieżkę dostępu (jeżeli plik znajduje się w podfolderze katalogu z programem, wystarczy podać fragment ścieżki zaczynając od nazwy podkatalogu). Przykład użycia tej funkcji ilustruje poniższy kod:

import std.stdio;
import std.file;

void main()
{
  if(exists("plik.txt"))
  {
    writefln("Plik istnieje");
  }
  else
  {
    writefln("Plik nie istnieje);
  }
}

Jeżeli program znajduje się w katalogu C:/mojProgram/ a plik w C:/mojProgram/mojePliki/plik.txt, możemy po prostu podać:

if(exists("/mojePliki/plik.txt"))

[edytuj] Odczytywanie danych z pliku

Moduł std.file umożliwia odczyt i zapis do pliku, ale wygodniejsze jest użycie strumieni (chociaż wymaga to zaimportowania kolejnego modułu i dopisania kilku linijek kodu więcej). Ten też sposób zostanie tutaj omówiony.

Strumień służący do odczytu i zapisu plików reprezentowany jest przez klasę File (więcej o klasach, konstruktorach itd. w rozdziałach poświęconych obiektowości). Należy więc utworzyć jej obiekt:

File plik = new File("plik.txt", FileMode.In);

Pierwszy parametr konstruktora zawiera nazwę pliku, który chcemy otworzyć - drugi, tryb. Tryb może być jedną z czterech wartości:

  • FileMode.In - tryb odczytu
  • FileMode.Out - tryb zapisu
  • FileMode.OutNew - tryb zapisu, wszelkie dane z pliku zostaną usunięte
  • FileMode.Append - tryb zapisu, nowe dane będą dopisywane na końcu

Aktualnie wykorzystujemy ten pierwszy. Przed otwarciem pliku do odczytu należy się upewnić, że ten plik istnieje. W przeciwnym razie podczas wykonania programu dostaniemy błąd.

Jeżeli już uda się otworzyć plik, możemy zacząć odczytywać z niego dane. Do odczytywania danych z pliku służą metody read():

void read(out int a);

void read(out double a);

Ta metoda ma swój odpowiednik dla każdego typu arytmetycznego oraz typów char i wchar oraz tablic char[] i wchar[] (które są napisami). Należy pamiętać, że liczby nie są wczytywane tekstowo, ale bajtami (wczytane zostaje tyle bajtów, ile mieści dany typ, a następnie przekształcone w wartość tego typu). Jako argument należy podać zmienną, w której ma znaleźć się odczytana wartość:

int a;
plik.read(a);

Tekst można również wczytywać linia po linii. Do tego służą metody char[] readLine() i wchar[] readLineW(). W odróżnieniu od read() ta metoda nie przyjmuje argumentów, tylko zwraca wartość:

char[] napis = plik.readLine();

Aby sprawdzić, ile bajtów danych jest jeszcze do odczytania z pliku, używamy metody available().

Kiedy już wczytamy wszystkie potrzebne dane i plik nie będzie nam dłużej potrzebny, zamykamy strumień używając metody close().

Znamy zatem wszystkie instrukcje, które są potrzebne, by odczytywać pliki. Możemy zatem przejść do praktyki. Poniższy przykład prezentuje program, który wczytuje plik i wyświetla jego zawartość w konsoli:

import std.stdio;                                   // musimy zaimportować ten moduł, zawiera funkcję writefln()
import std.file;                                    // zawiera funkcję exists()
import std.stream;                                  // zawiera klasę File i funkcje służące do odczytu pliku.

void main()
{
  if(!exists("plik.txt"))                           // Jeśli plik "plik.txt" nie istnieje
  {
    writefln("Plik nie istnieje!");                 // to poinformuj o tym
    return;                                         // i wyjdź z funkcji (wyjście z funkcji main wyłącza program)
  }

  File plik = new File("plik.txt", FileMode.In);    // otwórz plik do odczytu i utwórz strumień wczytujący
  char[] line;                                      // utwórz tablicę, w której będą przechowywane linie
  while(plik.available() > 0)                       // dopóki w pliku są jeszcze nieodczytane dane
  {
    line = plik.readLine();                         // wczytaj linię do zmiennej line
    writefln(line);                                 // wypisz na ekran zmienną line
  }
  plik.close();                                     // zamknij strumień
}

[edytuj] Zapis danych do pliku

Zapis danych do pliku przebiega dość podobnie, jak odczyt. Różnica polega na tym, że otwieramy plik do zapisu (używając trybów FileMode.Out, FileMode.OutNew lub FileMode.Append) i, zamiast read(), używamy metod write(). Ponadto nie musimy martwić się o to, czy plik istnieje, ponieważ - jeśli nie - zostanie automatycznie utworzony.

Metody write() są właściwie identyczne - również przyjmują jeden argument, który jest zapisywany w pliku. Podobnie też wyglądają metody writeLine() i writeLineW(), z tym, że ciąg znaków przekazujemy w argumencie.

Dodatkowo dostępne są metody writeString() i writeStringW(), które od writeLine() i writeLineW() różnią się tym, że nie wstawiają na końcu łamania wiersza.

Poniższy przykład prezentuje program, który wczytuje z klawiatury linię tekstu, a następnie zapisuje ją do pliku.

import std.stdio;
import std.stream;

void main()
{
  char[] line = readln()[0..length-1]        // z ciągu znaków wczytanego funkcją readln() obcinamy ostatni znak - łamanie wiersza
  File plik = new File("plik.txt", FileMode.Out);   // otwieramy plik do zapisu
  plik.writeLine(line);                             // zapisujemy wprowadzoną linię do pliku
  plik.close();                                     // zamykamy plik
}

[edytuj] Operacje w systemie plików

W celu wykonywania takich operacji jak zmiana nazwy, przenoszenie, kopiowanie czy plików i folderów należy się posłużyć odpowiednimi funkcjami zawartymi w module std.file. Aby utworzyć nowy plik, wystarczy otworzyć go do zapisu. Oto lista najważniejszych funkcji modułu std.file (w argumentach podajemy nazwy plików):

  • rename(char[] src, char[] dest) - zmiana nazwy pliku z src na dest
  • remove(char[] name) - usunięcie pliku
  • copy(char[] src, char[] dest) - kopiowanie pliku z src do dest (aby przenieść plik, należy go skopiować, a następnie usunąć plik źródłowy)
  • ulong getSize(char[] name) - zwraca rozmiar pliku w bajtach
  • bool exists(char[] name) - sprawdza, czy plik istnieje (przykład przy odczycie z pliku)
  • bool isDir(char[] name) - sprawdza, czy nazwa wskazuje na katalog
  • bool isFile(char[] name) - sprawdza, czy nazwa wskazuje na plik
  • mkdir(char[] pathname) - tworzy katalog.
  • mkdirRecurse(char[] pathname) - tworzy katalog oraz wszystkie nieistniejące katalogi nadrzędne.
  • rmdir(char[] pathname) - usuwa katalog
  • rmdirRecurse(char[] pathname) - usuwa katalog wraz z całą zawartością
  • immutable(char[][]) listdir(char[] pathname) - zwraca zawartość folderu (nazwy plików w tablicy)
  • immutable(char[][]) listdir(char[] pathname, char[] pattern) - zwraca wszystkie nazwy plików pasujące do wyrażenia pattern (w tym wyrażeniu można stosować konstrukcje typu *.d)