poprzedni temat | w górę | następny temat

Pliki i funkcje wejścia/wyjścia

Otwieranie i zamykanie pliku

Czytanie i zapisywanie do pliku wymaga jego uprzedniego otwarcia. Plik możemy otworzyć funkcją fopen, której musimy podać dwa argumenty:

Funkcja fopen zwraca uchwyt do otwartego pliku, jeśli czynność się powiodła. W przeciwnym razie zwracana jest wartość NULL.

FILE * f;
f = fopen("program.c", "r");
if(!f){
  perror("fopen");
  exit(1);
}

Funkcja perror wypisuje przyczynę wystąpienia błędu na standardowe wyjście błędów.

Gdy już zakończymy wszystkie działania na pliku, należy go koniecznie zamknąć za pomocą funkcji fclose.

fclose(f);

Standardowe wejście, wyjście i wyjście błędów

Najczęściej w programie w momencie uruchamiania dostępne są trzy otwarte pliki: stdin, stdout, stderr. Pierwszy do odczytu, dwa pozostałe do zapisu. Można ich używać jak zwykłych plików, przy czym najczęściej są powiązane z wejściem z klawiatury oraz wyjściem na ekran.

Czytanie z pliku

Mając otwarty do odczytu plik, możemy z niego czytać dane.

Pamiętaj, że odczyt informacji z dysku jest wielokrotnie wolniejszy, niż odczyt z pamięci operacyjnej. Dlatego w miarę możliwości staraj się unikać np. dwukrotnego wczytywania zawartości pliku, jeśli możliwe jest jednokrotne wczytanie całego pliku do pamięci operacyjnej.

Funkcja fscanf działa analogicznie do funkcji scanf, przy czym różnica polega na tym, że jawnie podajemy, z jakiego pliku ma się odbywać czytanie.

int zmienna, wczytano;
wczytano = fscanf(f, "%d", &zmienna);
if(!wczytano){
  fprintf(stderr, "W pliku nie znaleziono liczby.\n");
} else if(wczytano == EOF) {
  fprintf(stderr, "Osiągnięto koniec pliku.\n");
} else {
  printf("Wczytana z pliku liczba to %d.", zmienna);
}

Znanej nam już funkcji fgets używamy do wczytywania całych linii tekstu.

char napis[100];
fgets(napis, 100, f);
printf("Wczytano linię: '%s'", napis);

Do wczytywania pojedynczych znaków możemy użyć funkcji fgetc.

int znak;
znak = fgetc(f);
if(znak == EOF){
  fprintf(stderr, "Został osiągnięty koniec pliku.\n");
} else {
  printf("Wczytano znak '%c'.\n", znak);
}

Warto zwrócić uwagę, że zmienna znak w powyższym przykładzie jest typu int. Tylko wtedy porównywanie jej wartości do EOF ma sens, ponieważ wartość EOF jest spoza zakresu typu char.

Zapisywanie do pliku

Analogicznie do funkcji printf działa funkcja fprintf.

fprintf(f, "%d %d\n", a, b);

Funkcja fputs zapisuje do pliku zawartość napisu.

fputs(napis, f);

Aby zapisać do pliku pojedynczy znak, możemy użyć funkcji fputc.

fputc(znak, f);

Większy przykład

Poniższy program wypisuje na standardowe wyjście zawartość pliku podanego jako argument programu.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]){
  if(argc < 2){
    fprintf(
      stderr,
      "Podaj nazwę pliku jako argument programu.\n"
    );
    exit(1);
  }
  FILE * f = fopen(argv[1], "r");
  if(!f){
    perror("fopen");
    exit(2);
  }
  int znak;
  while((znak = fgetc(f)) != EOF)
    fputc(znak, stdout);
  fclose(f);
  return 0;
}

Zadania

  1. Napisz program, który wypisze liczbę znaków w pliku, którego nazwa jest podana jako parametr wywołania programu.

  2. Napisz program, który jest uruchamiany z dwoma parametrami — nazwami plików. Program ma kopiować pierwszy plik do drugiego.

  3. Zmodyfikuj program z poprzedniego zadania tak, aby zawartość pierwszego pliku była dopisywana do drugiego.

  4. Napisz program, który jest uruchamiany z dwoma parametrami — nazwami plików. Program ma porównywać zawartość obu plików leksykograficznie. Jeśli pierwszy plik jest wcześniejszy, program ma wypisać liczbę -1. Jeśli oba pliki są jednakowe, program ma wypisać liczbę 0. Jeśli drugi plik jest wcześniejszy, program ma wypisać liczbę 1.

  5. Zdefiniuj typ strukturalny struct Miasto do przechowywania danych na temat miast: nazwy, powierzchni i ludności.

    Pobierz plik miasta.txt, w którym kolumny oznaczają odpowiednio: powierzchnię, ludność i nazwę miasta.

    Zdefiniuj funkcję struct Miasto *wczytane_miasto(FILE *plik), która wczyta z otwartego pliku dane na temat jednego miasta i zwróci je jako wskaźnik do dynamicznie zaalokowanej struktury typu struct Miasto. Jeśli nie udało się wczytać kolejnego miasta z powodu napotkania końca pliku, funkcja powinna zwrócić wartość NULL.

    Zdefiniuj funkcję, która przyjmuje jako parametr uchwyt do otwartego pliku. Funkcja, używając funkcji wczytane_miasto, ma wypełnić dynamiczną tablicę wskaźników do wartości typu struct Miasto. Nie znamy z góry liczby wierszy czyli miast w podanym pliku, więc funkcja powinna na bieżąco powiększać rozmiar tablicy za pomocą funkcji realloc, aby móc zapisać wskaźniki do kolejnych wczytywanych miast. Za wskaźnikiem do ostatniego wczytanego miasta w tablicy powinna się znaleźć wartość NULL jako znacznik końca tablicy. Funkcja ma zwrócić adres wspomnianej tablicy.

    Napisz funkcję, która otrzyma tablicę miast jak opisana powyżej i zwróci wskaźnik na miasto o największej gęstości zaludnienie.

    Napisz funkcję, która wypisuje na standardowe wyjście informacje o mieście, które dostała jako parametr — wskaźnik na strukturę struct Miasto.

    Napisz program, który, używając powyższych funkcji, wypisze miasto o największej gęstości zaludnienia.

poprzedni temat | w górę | następny temat