Środowisko programisty/C - pliki

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania

Wprowadzenie

Istnieją dwie metody dostępu do plików w C:

  1. Dostęp wysokopoziomowy, który umożliwia wygodne operowanie na plikach tekstowych
  2. Dostęp niskopoziomowy, który zapewnia dostęp do plików binarnych

Dostęp niskopoziomowy

open() i close()

Podczas dostępu niskopoziomowego pliki identyfikowane są przez liczby całkowite typu int. Do otwierania plików służy funkcja open(). Po zakończeniu operowania na pliku należy ten plik zamknąć aby zwolnić zasoby systemowe. Jeśli pliku nie uda się otworzyć, to funkcja open zwróci wartość mniejszą od zera. Typowa funkcja używająca plików wygląda zatem następująco:

int identyfikator_pliku;
identyfikator_pliku = open(... parametry ...)
if (identyfikator_pliku < 0) { .... obsłuż błąd } else
{
  ... rób coś z plikiem
  close(identyfikator_pliku)
}

Funkcja open przyjmuje zazwyczaj dwa parametry. Pierwszym z nich jest nazwa pliku, który ma być otwierany, a drugim tryb w jakim plik ma być otworzony. Tryb ten to jedna z trzech wartości:

  1. O_RDONLY - plik będzie otwarty w trybie tylko do odczytu
  2. O_WRONLY - plik będzie otwarty w trybie tylko do zapisu
  3. O_RDWR - plik będzie otwarty w trybie do odczytu i zapisu

dodatkowo parametr ten może być zsumowany bitowo z jedną z dodatkowych wartości:

  1. O_CREAT - utworzenie pliku, jeśli on nie istnieje; w tym przypadku należy funkcji open podać trzeci parametr, który określa prawa dostępu do tworzonego pliku
  2. O_EXCL - jeśli plik ma być utworzony mimo, że już istnieje, polecenie open zakończy się błędem
  3. O_APPEND - otwarcie pliku w trybie do dopisywania

Definicje wszystkich powyższych stałych są dostępne w pliku <fcntl.h>

Dodatkowe informacje o funkcji open udostępnia man 2 open.

Funkcja close przyjmuje jeden parametr - deskryptor otwartego pliku.

read() i write()

Funkcje read() o write() służą odpowiednio do czytania i zapisywania danych z pamięci do plików. Obie funkcje przyjmują po trzy parametry:

  1. Deskryptor pliku, z którego należy czytać lub do którego należy pisać
  2. Adres w pamięci od którego należy zacząć czytanie/pisanie
  3. Maksymalna liczba bajtów, które należy przeczytać/zapisać

Na poniższym przykładzie możemy zaobserwować, w jaki sposób można używać funkcji read i write.

#include <stdio.h>
#include <unistd.h> //funkcja write
#include <fcntl.h> // O_WRONLY O_CREAT
#include <sys/stat.h> //S_IRWXU)
#include <errno.h> // errno

int main()
{
  int fd;
  char* bufor = "Test";
  fd = open("test.bin", O_WRONLY|O_CREAT, S_IRWXU);
  if (fd < 0)
  {
    printf("Blad %d\n", errno);

  } else {
    write(fd, bufor, 4);
    close(fd);
  };
  fd = open("test.bin", O_RDONLY);
  if (fd < 0)
  {
    printf("Blad %d\n", errno);

  } else {
    read(fd, bufor, 4);
    close(fd);
  };
  return 0;
}

rename()

Funkcja rename(stara_nazwa, nowa_nazwa) zmienia nazwę pliku ze starej na nową.

unlink()

Funkcja unlink(nazwa_pliku) usuwa plik o podanej nazwie.

lseek()

Funkcja lseek przesuwa miejsce, z którego będą czytane (lub zapisywane) dane w pliku. Przyjmuje ona trzy parametry:

  1. Deskryptor pliku
  2. Liczbę bajtów przesunięcia
  3. Tryb przesunięcia, który może być jedną z trzech następujących wartości:
    1. SEEK_SET - liczba bajtów będzie liczona od początku pliku
    2. SEEK_CUR - przesunięcie nastąpi od aktualnej pozycji
    3. SEEK_END - liczba bajtów będzie liczona od końca pliku (sensowne jest podanie ujemnej liczby bajtów przesunięcia)

Dostęp wysokopoziomowy

fopen() i fclose()

Funkcje fopen() i fclose() są zdefiniowane w pliku stdio.h. Wynikiem działania funkcji fopen() jest wskaźnik na strukturę typu FILE opisującą plik. Funkcja fopen potrzebuje dwóch parametrów, pierwszym z nich jest nazwa pliku a drugim tryb, w jakim plik ma być otwierany.

Tryby otwierania pliku
Tryb Opis
r Otwarcie istniejącego pliku do odczytu
w Utworzenie pustego pliku (kasuje istniejący plik) do zapisu
a Otwarcie pliku do dopisywania danych, nie tworzy nowego pliku, jeśli on nie istnieje
r+ Otwarcie istniejącego pliku do odczytu i zapisu
w+ Utworzenie pustego pliku (kasuje istniejący plik) do odczytu i zapisu
a+ Otwarcie pliku do czytania i dopisywania; tworzy nowy plik, jeśli ten nie istnieje

Jeśli otwarcie pliku się nie powiedzie, to wynikiem funkcji jest NULL.

fprintf() i fscanf()

Funkcje fprintf i fscanf działają identyczne jak printf i scanf, tylko operują na plikach. Zobaczmy następujący przykład:

#include <stdio.h>
int main()
{
  FILE *plik;
  plik = fopen("test2.txt", "w+");
  if(plik == NULL) {
    printf("Blad otwarcia pliku\n");
    return 1;
  };
  fprintf(plik, "Napis %d", 10);
  fclose(plik);
  return 0;
}

getc(), ungetc(), putc(), fgetc(), fputc() , feof()

Funkcje getc() i fgetc() wczytują kolejny znak z pliku. Ich wynikiem jest zawsze liczba typu int, której wartością jest EOF w przypadku, kiedy plik się skończył. Poniższy program liczy długość pliku:

#include <stdio.h>
int main()
{
  FILE *plik;
  int d, i;
  plik = fopen("test2.txt", "r");
  if(plik == NULL) {
    printf("Blad otwarcia pliku\n");
    return 1;
  };
  d = -1;
  do {
    d++;
    i = getc(plik);
  } while (i != EOF);
  printf("Plik ma %d bajtow\n", d);
  fclose(plik);
  return 0;
}

Funkcja ungetc() cofa wskaźnik pliku o jeden znak, czyli w pewnym sensie niweluje znaczenie ostatniego wywołania getc()

Funkcje putc() i fputc() wypisują znak do pliku.

Funkcja feof() sprawdza, czy został osiągnięty koniec pliku.

Poniższy program zamienia w pliku wszystkie wystąpienia a na b.

#include <stdio.h>
int main() 
{
  FILE *plik;
  plik = fopen("zamiany.txt", "r+");
  if(plik == NULL) { 
    printf("Blad otwarcia pliku\n");
    return 1;
  };
  while (!feof(plik)) {
    if (getc(plik) == 'a') {
      ungetc('a', plik);
      putc('b', plik);
    };
  };
  fclose(plik);
  return 0;
}

fputs() i fgets()

Funkcje fputs() i fgets() wypisują i wczytują łańcucj znaków do pliku. Użycie fgets jest bardzo niebezpieczne, gdyż nie ma żadnej gwarancji jak długi łańcuch zostanie wczytany.