Programowanie w C Wykład 3 Operacje we/wy - obsługa plików
Buforowanie Wejścia/Wyjścia Bufor (buffer ) -obszar pamięci służący do tymczasowego przechowywania danych przed przesłaniem ich do miejsca przeznaczenia Posługując się buforami system operacyjny zmniejsza ilość odwołań do fizycznych urządzeń Wejścia/Wyjścia. Strumienie We/Wy są buforowane w sposób domyślny Fizyczny zapis do pliku -buffer flush Opróżnienie bufora i przesłanie danych na dysk można wymusić funkcją fflush() int fflush(FILE *stream); Jeśli wykonanie funkcji fflush() zakończyło się poprawnie to jest zwracana wartość 0, w przeciwnym przypadku - wartość EOF
Wskaźniki typu FILE Struktura FILE służy do zarządzania plikami i jest zdefiniowana w pliku stdio.h Wskaźnik typu FILE -file pointer, służy do odwoływania się do konkretnego pliku dyskowego. W obrębie struktury FILE można znaleźć: rozmiar pliku, znacznik pozycji w pliku , adres bufora danych itp. typedef struct _iobuf { char* _ptr; int _cnt; char* _base; int _flag; int _file; int _charbuf; int _bufsiz; char* _tmpfname; } FILE;
Operacje wejścia/wyjścia, strumienie, pliki Gdy program rozpoczyna działanie otwarte są trzy standardowe strumienie wejścia/wyjścia: stdin - standardowe wejście (konsola - klawiatura) stdout - standardowe wyjście (konsola - monitor) stderr - standardowe wyjście dla komunikatow o błędach (konsola - monitor) definicje powyższych strumieni oraz prototypy funkcji wejścia/wyjścia umieszczone są w pliku nagłowkowym stdio.h /* * The three standard file pointers provided by the run time library. * NOTE: These will go to the bit-bucket silently in GUI applications! */ #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #define stdin (&_iob[STDIN_FILENO]) #define stdout (&_iob[STDOUT_FILENO]) #define stderr (&_iob[STDERR_FILENO]) Warto zauważyć, że konstrukcja: fprintf (stdout, "Hej, ja działam!") ; jest równoważna konstrukcji printf ("Hej, ja działam!"); Podobnie jest z funkcją scanf(): fscanf (stdin, "%d", &zmienna); działa tak samo jak scanf("%d", &zmienna);
Wszystkie operacje związane z przetwarzaniem pliku zazwyczaj składają się z trzech części: 1. Otwarcie pliku (strumienia): - funkcje: fopen() 2. Operacje na pliku (strumieniu), np. czytanie, pisanie: - funkcje dla plików tekstowych: fprintf(), fscanf(), fgetc(), fputc(), fgets(), fputs()… - funkcje dla plików binarnych: fread(), fwrite(), … 3. Zamknięcie pliku (strumienia): - funkcja: fclose(). Każdy strumień może pracować w trybie binarnym lub tekstowym.
Funkcje otwarcia i zamknięcia pliku #include <stdio.h> FILE* fopen (const char* namefile, const char* mode); namefile - nazwa pliku mode - tryb ustawienia pliku: r - do czytania; zbiór musi istnieć w - otwiera pusty zbiór do zapisu; jeśli plik istnieje kasuje jego zawartość a - do zapisu na końcu zbioru; jeśli plik nie istnieje tworzy nowy r+ - otwiera istniejący plik do zapisu i odczytu w+ - otwiera pusty plik do zapisu i odczytu; istniejący plik kasuje a+ - otwiera do odczytu i zapisu na końcu pliku; jeżeli plik nie istnieje to go tworzy. b - otwiera plik w trybie binarnym a i a+ - zapisuje zawsze na końcu mimo użycia funkcji pozycjonującej fseek() w i w+ - zawsze kasuje istniejący plik
Plik dane.txt otwierany jest w trybie tekstowym tylko do czytania. Przykład: Plik dane.txt otwierany jest w trybie tekstowym tylko do czytania. Plik data.bin otwierany jest w trybie binarnym tylko do zapisu przy podawaniu ścieżki dostępu do pliku, zamiast jednego znaku \ należy podawać dwa znaki - \\ FILE *stream1, *stream2; stream1 = fopen(”dane.txt”,”r”); stream2 = fopen(“c:\\baza\\data.bin”,”wb”);
Funkcje otwarcia i zamknięcia pliku int fclose (FILE* pfile); Funkcja zwraca wartość: 0 – jeżeli zamknięcie pliku zostało wykonane poprawnie; EOF – w przypadku błędu operacji zamykania pliku (-1). #include <stdio.h> void main() { FILE* pfile = fopen(”plik.txt”, ”rt”); if (pfile) printf(”Plik otwarty.\n”); fclose(pfile); } else printf(”Błąd otwarcia pliku!\n”);
Pliki tekstowe i binarne Wyrożnia się dwa rodzaje plików: tekstowe i binarne Elementami pliku tekstowego są wiersze, mogą one mieć rożną długość W systemach DOS/Windows każdy wiersz pliku zakończony jest parą znaków: CR, ang. carriage return - powrot karetki, kod ASCII – 13(10) (0D16) LF, ang. line feed - przesunięcie o wiersz, kod ASCII – 10(10) (0A16) W czasie wczytywania tekstu z pliku do pamięci komputera znaki CR i LF zastępowane są jednym znakiem – LF znak LF w języku C reprezentowany jest przez \n, zaś CR - przez \r przy zapisywaniu łańcucha znaków do pliku tekstowego mamy sytuację odwrotną - znak LF zastępowany jest parą CR i LF #include <stdio.h> int main() { printf("\\n --> %d %X\n",'\n','\n'); \\ \n --> 10 A printf("\\r --> %d %X\n",'\r','\r'); \\ \r --> 13 D return (0); }
Funkcje fgetc, fputc, fgets, fputs #include <stdio.h> Odczyt znaku ze strumienia: int fgetc(FILE* stream); Fgetc czyta znak ze strumienia stream jako unsigned char i przekształca do int . Zzwraca znak czytany lub EOF jeśli powstał błąd lub napotkano EOF (end of file). Zapis znaku do strumienia: int fputc(int c, FILE* stream); Fputc zapisuje pojedynczy znak do zbioru. Zwraca pisany znak lub EOF jeśli powstał błąd lub napotkano EOF.
Wyświetlenie zawartości pliku tekstowego #include <stdio.h> #include <stdlib.h> int main() { FILE *plik; int chr; plik=fopen("plik.txt","r+"); while ((chr=fgetc(plik))!=EOF) printf("%c",chr); fclose(plik); system("pause"); return 0; }
Odczyt łańcucha znaków ze strumienia: char* fgets(char* string, int n, FILE* stream); Odczytuje ze strumienia stream ciąg znaków aż do: - pierwszego znaku '\n' (włącznie) - do końca strumienia - lub gdy przeczytano n-1 znaków i wstawia je do stringu dołączając na końcu '\0‘. Zapis łańcucha znaków do strumienia: int fputs(char* string, FILE* stream); Wysyła string do strumienia wskazanego parametrem stream, bez końcowego znaku '\0'. Zwraca EOF jeśli powstał błąd, w przeciwnym razie wartość różną od 0.
Formatowany zapis do zbioru int fscanf (FILE *plik, const char *format, wskaźnik, wskaźnik, ...) Funkcja odczytuje ze wskazanego pliku ciągi znaków, dokonuje ich konwersji na wartości binarne i zapamiętuje te wartości w miejscach pamięci wskazanych za pomocą argumentów będących wskaźnikami. Wartością argumentu format jest ciąg wzorców konwersji, których postać jest taka sama jak dla funkcji scanf. Zwraca liczbę zapisanych znaków lub -1 gdy wystąpił błąd. int fprintf (FILE *plik, const char *format, wyrażenie, wyrażenie, ...) Funkcja zapisuje we wskazanym pliku ciągi znaków zadane za pomocą wyrażeń będących jej argumentami. Sposób konwersji wartości wyrażeń na ciągi znaków określa argument format, będący ciągiem znaków zawierającym znaki wpisywane bezpośrednio do pliku dyskowego i wzorce konwersji. Postać wzorca konwersji jest taka sama jak dla funkcji printf. Zwraca liczbę przepisanych danych wejściowych, gdy wystąpił błąd: 0 lub EOF napotkano koniec zbioru.
Zapisanie do pliku tekstowego 10 wygenerowanych pseudolosowo liczb #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { FILE *stream; int i; if ((stream=fopen("plik.txt","w")) == NULL) printf("Blad otwarcia pliku\n"); return (-1); } srand(time(NULL)); for (i=0; i<10; i++) fprintf(stream,"%d\n",rand()%100); fclose(stream); system("pause"); return 0;
Program zapisujący do pliku tekstowego wszystkie liczby podzielne przez 5 z zakresu [10,50] oraz wyświetlający zawartość pliku na ekranie. #include <stdio.h> void zapisz (FILE *plik) { for (int i=10;i<=50;i+=5) fprintf(plik,"%d\n",i); } void wyswietl (FILE *plik) int liczba; rewind(plik); while (!feof(plik)) { fscanf(plik,"%d\n",&liczba); printf(„”%d\t”,liczba); } printf(”\n”); } int main (void) FILE *plik; plik = fopen ("C:\\liczby.txt","wt+"); if (plik==NULL) printf(”Plik nie zostal otwarty \n”); else { zapisz(plik); wyswietl(plik); fclose(plik); } getch(); return 0; }
Operacje na plikach binarnych size_t fread(void *p, size_t s, size_t n, FILE *stream); funkcja fread() czyta n elementow o rozmiarze s bajtow każdy z pliku wskazywanego przez stream i umieszcza odczytane dane w obszarze pamięci wskazywanym przez p size_t fwrite(const void *p, size_t s, size_t n, FILE *stream); funkcja fwrite() zapisuje n elementow o rozmiarze s bajtow każdy do pliku wskazywanego przez stream biorąc dane z obszaru pamięci wskazywanego przez p Obie funkcje, fread() i fwrite(), zwracają liczbę przetworzonych (zapisanych lub odczytanych elementów) - liczba ta jest rożna od n, gdy wystąpił błąd końca strumienia (czytanie) lub błąd zapisu.
Operacje na plikach binarnych - przykład #include <stdio.h> int main() { FILE *plik; int d1 = 15, d2; float tab1[5]={1.0,2.0,3.0,4.0,5.0}; float tab2[5]; plik = fopen("data.bin","wb"); fwrite(&d1,sizeof(int),1,plik); fwrite(tab1,sizeof(float),5,plik); fclose(plik); plik = fopen("data.bin","rb"); fread(&d2,sizeof(int),1,plik); fread(tab2,sizeof(tab2),1,plik); return 0; }
FUNKCJE USTALAJĄCE AKTUALNĄ POZYCJĘ PLIKU I BADAJĄCE OSIĄGNIĘCIE KOŃCA PLIKU long ftell (FILE *plik) Funkcja odczytuje aktualną pozycję pliku wyrażoną w bajtach. Wynikiem funkcji ftell jest liczba określająca aktualną pozycję pliku lub wartość –1L w wypadku wystąpienia błędu. int fseek (FILE *plik, long pozycja, int cel) Funkcja ustala aktualną pozycję pliku wskazanego za pomocą argumentu plik. Wartością argumentu pozycja powinno być 0 lub wartość uzyskana po wykonaniu funkcji ftell. Wartość parametru pozycja może być również ujemna (przesunięcie wstecz), jak i dodatnia (przesunięcie w przód). Argumentem cel może być jedna ze stałych: SEEK_SET 0 początek pliku SEEK_CUR 1 aktualna pozycja pliku SEEK_END 2 koniec pliku Wynikiem funkcji fseek jest 0, gdy aktualna pozycja pliku została poprawnie ustalona. fseek (fp, 0, SEEK_END); /* ustawiamy wskaźnik na koniec pliku */ void rewind (FILE *plik) Funkcja ustala aktualną pozycję na początku pliku wskazanego za pomocą argumentu plik. int feof (FILE *plik) Funkcja odczytuje stan znacznika końca pliku. Gdy podczas ostatniej operacji odczytu napotkano koniec pliku, to wynikiem funkcji feof jest wartość niezerowa, w przeciwnym wypadku wynikiem jest 0.
int fgetpos (FILE* file, fpos_t* pos); Funkcja umieszcza w pos aktualną pozycję wskaźnika do pliku file. Funkcja zwraca zero gdy została wykonana pomyślnie, EOF w przypadku wystąpienia błędu, kod błędu umieszczany jest w zmiennej globalnej errno.
Obsługa błędów Kiedy jakaś operacja nie powiedzie się, pojawia się błąd i dla danego strumienia (pliku) ustawiany jest wskaźnik błędu. Informacje o ostatnio popełnionym błędzie przechowywane są w zmiennych [ int errno, _doserrno, sys_nerr (w stdlib.h)] gdzie: errno - numer błędu operacji matematycznych lub systemu DOS; _doserrno - numer błędu dla systemu DOS; sys_nerr - zawiera liczbę komunikatów w tablicy sys_errlist[]; komunikaty znajdują się w tablicy char *sys_errlist[]; funkcja perror jest używana do drukowania komunikatów.
Obsługa błędów
Obsługa błędów
Program arg. c ilustruje przekazywanie argumentów do programu Program arg.c ilustruje przekazywanie argumentów do programu. Przeanalizujmy go w kilku krokach Włączamy znany nam dobrze plik nagłówkowy stdio.h, odpowiedzialny za funkcje wejścia/wyjścia Używamy rozwiniętej definicji funkcji main. Jest to ta sama główna funkcja programu, co wcześniej, lecz umożliwia przekazywanie argumentów do programu. Nagłówek funkcji zawiera dwa argumenty: int argc - liczba przekazywanych argumentów + 1 char *argv[] - tablica wskaźników, które są typu znakowego; zawiera ona aktualnie przekazane argumenty. Przykładowo, jeśli skompilujemy program i wywołamy go z linii poleceń z argumentami jeden i dwa, to tablica argv[] będzie zawierała: argv[0] Nazwa uruchomionego programu. argv[1] Pierwszy argument, u nas jeden. argv[2] Drugi argument, tutaj dwa. Definiujemy zmienną lokalną (dostępną tylko wewnątrz funkcji main) o nazwie licznik; będzie ona pomocna przy wyświetlaniu nazw poszczególnych argumentów. W pętli for wykonywana jest funkcja printf wyświetlająca numer argumentu oraz jego nazwę. Pamiętajmy, że w C tablice o rozmiarze n elementów są indeksowane od 0 do n - 1.
/* Przyklad przekazywania argumentow do programu */ #include <stdio.h> int main(int argc, char *argv[]) { int licznik; for (licznik = 0; licznik < argc; licznik++) { printf("Argument %d = %s \n", licznik, argv[licznik]); } while (getchar() != '\n'); return 0; }
Argumenty funkcji ”main” #include <conio.h> #include <stdio.h> int main (int argc, char * argv[]) { int i; switch (argc) case 1: printf ("Program uruchomiono bez parametrow!\n"); break; default: printf ("Parametry uruchomieniowe programu:\n"); for (i = 1; i < argc; i++) printf ("%d: %s\n", i, argv[i]); } printf ("\nNacisnij dowolny przycisk, aby zakonczyc...\n"); getch (); return 0;