Operacje na plikach

Praca na plikach możliwa jest dzięki funkcjom dostępnym w bibliotece stdio.h. Chcąc odczytać dane z pliku (lub zapisać dane do pliku) najpierw należy uzyskać dostęp do pliku za pomocą funkcji fopen(), która otwiera plik i zwraca zmienną typu FILE*. Uzyskany w ten wskaźnik daje dostęp do pliku pozwalając na sekwencyjny odczyt lub zapis danych do wskazanego pliku. Jest on argumentem wszystkich funkcji operujących na plikach. Po zakończeniu pracy na pliku należy go zamknąć za pomocą funkcji fclose().

Funkcja fopen otwiera plik o nazwie nazwa_pliku.

FILE *fopen(char *nazwa_pliku, char *tryb);

Pierwszy argument nazwa_pliku jest napisem zawierającym ścieżkę do pliku. Może to być ścieżka bezwzględna (np. w Windows "C:\\Users\a.txt", w Unix/Linux "/home/user/a.txt") lub względna zależna od bieżącego katalogu roboczego. Jeżeli podamy tylko nazwę pliku (bez pełnej ścieżki) to powinien on się znajdować w tym samym katalogu co plik wykonywalny.

Argument tryb to napis, który określa tryb dostępu do pliku. Oto lista dostępnych trybów dla plików tekstowych:

Tryb
"r" Odczyt pliku od początku.
Jeżeli plik nie istnieje to funkcja fopen zwróci NULL.
"w" Zapis (nadpisz od początku)
Jeżeli plik nie istnieje to zostanie utworzony.
Jeżeli plik istnieje to jego zawartość zostanie skasowana
"a" Dodaj na końcu

Uwaga: w przypadku wystąpienia błędu przy próbie uzyskania dostępu do pliku funkcja fopen zwraca wartość NULL. Przyczyną błędu może być zła nazwa pliku lub niepoprawna ścieżka, brak uprawnień do otwierania (lub edycji) pliku lub nawet brak miejsca na nośniku. Ponieważ tego typu sytuacje mogą występować często dlatego należy zawsze sprawdzić czy operacja fopen zakończyła się sukcesem.

Po zakończeniu pracy na pliku należy go zamknąć za pomocą funkcji fclose

int fclose (FILE *plik);

Pojedynczy znak z pliku możemy pobrać za pomocą funkcji fgetc.

int fgetc(FILE *f);

Argumentem funkcji jest wskaźnik f do pliku uzyskany wcześniej za pomocą fopen. Plik powinien być otworzony w trybie do odczytu. Funkcja fgetc zwraca wartość EOF, gdy strumień danych z pliku jest pusty (koniec pliku).

Funkcja fputc umieszcza znak c w pliku f

int fputc(char c, FILE* f);

Funkcja feof pozwala sprawdzić, czy podczas odczytu pliku f dotarliśmy do jego końca.

int feof(FILE *plik);

Funkcja zwraca wartość 1 gdy osiągnięto koniec pliku, w przeciwnym wypadku zwracana jest wartość 0.

Przykład 1: Czytanie sekwencji znaków z pliku

Poniższy program odczytuje wszystkie znaki (bajty) z pliku plik.txt i wyświetla je na ekranie. Jeżeli plik wejściowy nie istnieje lub gdy nastąpił jakikolwiek błąd przy otwieraniu pliku wówczas wyświetlany jest komunikat o błędzie i program kończy swoje działanie.

znaki.c
#include<stdio.h>
 
int main()
{
   FILE *plik = NULL;
   int znak;
 
   plik = fopen( "plik.txt", "r" );
   if( plik == NULL )
   {
      printf("Wystapil blad otczytu pliku\n");
      return 1;
   }
 
   while( feof(plik) == 0 )
   {
      znak = fgetc(plik);
      if (znak != EOF) printf("%c", znak);
   }
   fclose( plik );
 
   return 0;
}

Przykładowy plik wejściowy: plik.txt. Należy umieścić go w katalogu w którym znajduje się plik wykonywalny programu.

Funkcja fgets odczytuje linię tekstu z pliku f i umieszcza napis w tablicy s. Argument drugi n ogranicza maksymalna liczbę znaków jaką wczyta funkcja zapobiegając przepełnieniu bufora dla zbyt długich linii.

char* fgets (char *s, int n, FILE *f);

Jest to więc odpowiednik funkcji czytaj_linie z poprzednich zajęć, który odczytuje linie z pliku.

Przykład 2: Szukanie wzorca (grep)

Poniższy program otwiera plik o nazwie abacus.txt i wypisuje na ekranie linie, które zawierają wyraz abacus.

grep.c
#include<stdio.h>
#include<string.h>
 
#define MAXLINE 1000
 
int main()
{
   char *wzor = "abacus";
   FILE *plik = NULL;
   char linia[MAXLINE];
 
   plik = fopen( "abacus.txt", "r" );
   if( plik == NULL )
   {
      printf("Wystapil blad otczytu pliku\n");
      return 1;
   }
 
   while( feof(plik) == 0 )
   {
      fgets(linia, MAXLINE, plik);
      if (strstr(linia, wzor)) printf(linia);
   }
   fclose( plik );
 
   return 0;
}

Przykładowy plik wejściowy: abacus.txt. Należy umieścić go w katalogu, w którym znajduje się plik wykonywalny programu.

Funkcja fprintf służy do zapisu tekstu do pliku z formatowaniem. Jej działanie jest takie samo jak funkcji printf() z jedną rożnica - formatowany tekst zamiast na ekran trafia do pliku f otworzonego w trybie do zapisu.

int fprintf(FILE *f, char *format, ...);

Przykład 3: Zapis wartości do pliku

Poniższy program demonstruje zapis wartości całkowitej 42, wartości rzeeczywistej 4.13 oraz napisu "Witaj swiecie" do pliku o nazwie "output.txt".

zapis.c
#include<stdio.h>
int main()
{
   int a = 42;
   float x = 3.14;
   char napis[]="Witaj swiecie\n";
   FILE* plik;
 
   plik = fopen("output.txt", "w");
   if (plik)
   {
      fprintf(plik, "a = %d\n", a);
      fprintf(plik, "x = %f\n", x);
      fprintf(plik, "%s\n",  napis);
 
      fclose(plik);
   }
   else printf("Blad zapisu do pliku\n");
 
   return 0;
}

Do odczytu z pliku danych z uwzględnieniem ich formatowania służy funkcja fscanf. Działa ona identycznie jak znana nam już funkcja scanf, z tą rożnica, że odczyt danych następuje z pliku f otworzonego w trybie do odczytu.

int fscanf(FILE *f,  char *format, ...);

Przykład 4: Odczyt wartości z pliku

Poniższy program demonstruje sposób użycia funkcji fscanf do odczytania danych zapisanych za pomocą programu z poprzedniego przykładu.'

odczyt.c
#include<stdio.h>
 
int main()
{
   int a;
   float x;
   char napis[100]; 
   FILE* plik;
 
   plik = fopen("output.txt", "r");
   if (!plik)
   {
      printf("Blad odczytu do pliku\n");
      return 1;
   }
 
   fscanf(plik, "a = %d\n", &a);
   fscanf(plik, "x = %f\n", &x);
   fgets(napis, 100, plik);
   fclose(plik);
 
   printf("a = %d\n", a);
   printf("x = %f\n", x);
   printf("%s\n", napis);
 
   return 0;
}

Przykładowy plik z danymi do odczytu: output.txt

Więcej informacji o funkcjach służących do manipulacji na plikach znajdziesz w dokumentacji biblioteki stdio.h

Napisz program, który zapisze do pliku tekstowego o nazwie liczby.txt ciąg n losowych liczb rzeczywistych z rozkładu jednostajnego z przedziału od a do b. Liczby umieszczane są w pliku w kolejnych liniach. Do uzyskania ciągu liczb (pseudo)losowych użyj funkcję rand() z biblioteki stdlib.h.

Dane: liczba całkowita n określająca ilość wygenerowanych liczb oraz dwie liczby rzeczywiste a i b określające przedział generowanych liczb.
Wynik: plik liczby.txt zawierający n losowych liczb rzeczywistych z przedziału od a do b umieszczonych w kolejnych liniach

Przykład
Dla danych wejściowych:

n = 10
a = -1
b = 3

program utworzy plik output.txt zawierający 10 losowych wartości rzeczywistych z przedziału [-1, 3] Przy każdym uruchomieniu otrzymywana jest inna losowa sekwencja liczb. Przykładowy wynik mógłby wyglądać tak:

2.168048
0.188781
-0.507287
1.787186
2.709183
2.666946
-0.229189
-0.980685
0.169793
-0.582870

Napisz program, który utworzy w bieżącym katalogu plik o nazwie output.txt zawierający zaszyfrowaną treść z pliku input.txt. Szyfrowanie wykonywane jest poprzez zamianę miejscami kolejnych par znaków (bajtów). Pamiętaj o tym, że plik może zawierać nieparzystą liczbę znaków, wówczas ostatni znak pozostaje na swoim miejscu. W przypadku problemów z odczytaniem pliku wejściowego program wyświetla stosowny komunikat błędu i kończy swoje działanie.

Przykład
Dla pliku wejściowego input.txt o treści:

Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.

program utworzy plik o nawie output.txt zawierający taką treść

oLer mpius modol ris tmate
,ocsnceetut rdapisiicgne il,ts
ded  oiesuom detpmroi cndidinu
ttul baro eted loro eamng alaqiau.