Laboratorium Podstaw Informatyki Strona 1 Laboratorium Podstaw Informatyki Kierunek Elektrotechnika Obsługa plików Kraków 2013
Laboratorium Podstaw Informatyki Strona 2 Obsługa plików Zanim będziemy mogli czytać lub pisać coś z/do pliku, plik musi być otwarty przy pomocą funkcji fopen. Wywołanie funkcji fopen w programie ma postać: fp = fopen(name, mode); Pierwszym argumentem fopen jest nazwa pliku w postaci tekstowej. Argument drugi (mode) informuje o trybie dostępu do pliku. Dopuszcza się następujące tryby: czytanie "r", pisanie "w", i dopisywanie "a". Dodatkowo można dołączyć literkę "b" (np. tak "rb"), która zapewni, że plik zostanie otwarty w trybie binarnym. Tryb ten używany jest do otwierania plików zawierających dane binarne tj np. obrazy (*.bmp, *.jpg), dźwięki (*.wav, *.mp3), pliki wykonywalne (*.exe). Funkcja fopen zwraca wskaźnik do pliku (ściśle do struktury opisującej otwarty plik), który przypisujemy do zmiennej fp. Oczywiście zmienna fp powinna być wcześniej zadeklarowana: Deklaracja ta mówi, że zmienna fp jest wskaźnikiem do obiektu typu FILE. Wywołanie funkcji fopen może się zakończyć błędem, wtedy funkcja zwraca wartość NULL. Gdy plik jest już otwarty można z niego czytać lub pisać. Najprostszymi funkcjami przeznaczonymi do tego są fgetc i fputc. Funkcja fgetc zwraca kolejny znak wczytany z pliku np. c = fgetc(fp); Gdy nie ma już więcej znaków funkcja kończy działanie i zwraca wartość EOF. Wartość EOF jest stałą zdefiniowaną w pliku nagłówkowym. Jej wartością zazwyczaj jest -1, stąd typem wartości zwracanym przez funkcję fgetc musi być int. Z tego powodu zmienna c również powinna być typu int. Funkcja fputc działa przeciwnie, tzn. wstawia znak c do pliku fp: fputc(c, fp); Oprócz funkcji fputc i fgetc przydatne są funkcje fprintf oraz fscanf będące odpowiednikami funkcji printf i scanf. Przykładowo, instrukcja fprintf(f1, "masa obiektu wynosi: %f [Kg]\n", masa); powoduje zapisanie do pliku identyfikowanego przez zmienną f1 tekstu "masa obiektu wynosi: ", następnie wypisywana jest wartość wyrażenia zmiennoprzecinkowego (w naszym wypadku wartość zmiennej masa), potem jeszcze "[Kg]" i na końcu, kursor przesuwany jest do nowej linii. Podobnie, instrukcja fscanf(f2, "%s%d", txt, &x); gdzie char txt[100]; int x; powoduje wczytanie z pliku, identyfikowanego przez zmienną f1, ciągu znaków (zakończonego znakiem białym) i liczby dziesiętnej. Na poniższych dwóch przykładach zanalizujemy działanie powyższej instrukcji. Przypadek 1. W pliku znajduje się ciąg znaków "abc10" potem jedna spacja potem kolejny ciąg znaków tj. "11". Zawartość pliku pokazana jest poniżej: abc10 11 Wykonanie instrukcji fscanf z parametrami jak powyżej spowoduje umieszczenie w zmiennej txt ciągu znaków "abc10", zaś w zmiennej x liczby 11.
Laboratorium Podstaw Informatyki Strona 3 Przypadek 2. W pliku znajduje się ciąg znaków "abc" potem jedna spacja potem kolejny ciąg znaków tj. "10", potem jedna spacja, a potem jeszcze "11". Zawartość pliku pokazana jest poniżej: abc 10 11 Wykonanie instrukcji fscanf z parametrami jak powyżej spowoduje umieszczenie w zmiennej txt ciągu znaków "abc", zaś w zmiennej x liczby 10. Przykład: Zadaniem programu jest wyświetlenie zawartości tekstowego pliku "plik.txt" na ekran. Uwaga: plik należy wcześniej utworzyć korzystając z edytora ASCII (np. notatnik). #include <stdio.h> main( ) { fp = fopen("c:\\plik.txt", "r"); if(fp == NULL) { printf("nie udalo się otworzyc pliku: c:\\plik.txt \n"); return -1; while((c=fgetc(fp))!=eof) putc(c, stdout); fclose(fp); return 0; stdout jest predefiniowanym strumieniem wyjściowym otwieranym automatycznie po uruchomieniu programu. Innym tym razem wejściowym (klawiatura) predefiniowanym strumieniem jest stdin. Zadanie 1: Napisz program kopiuj, który skopiuje zadany plik wejściowy na plik wyjściowy używając omówionych powyżej funkcji. Zadanie 2: Napisz program, który dla podanego pliku tekstowego (np. poprzedniego "plik.txt") policzy histogram, czyli częstość wystąpienie poszczególnych literek w nim. Uwaga warto skorzystać z wyników zadania z poprzednich zajęć. Często czytanie po jednym znaku jest nie wygodne. Dlatego do czytania i pisania większych porcji danych z/do pliku służą odpowiednio funkcje fread i fwrite. Deklaracja funkcji fread zamieszczona w zbiorze nagłówkowym stdio.h ma postać: size_t fread(void *ptr, size_t size, size_t n, FILE *stream); Oznacza ona, że funkcja fread czyta, z pliku określonego przez stream (to samo co fp poprzednio, ale inna nazwa - popatrz na typ), podaną ilość elementów (n) o jednakowej wielkości (size). Funkcja zwraca ilość faktycznie przeczytanych elementów - nie bajtów. Znaczenie argumentów jest następujące:
Laboratorium Podstaw Informatyki Strona 4 void *ptr wskaźnik na blok pamięci do którego zostaną zapisane przeczytane dane size_t size rozmiar pojedynczego elementu size_t n ilość elementów do przeczytania FILE *stream wskaźnik do pliku z którego będziemy czytać Deklaracja funkcji fwrite zamieszczona w zbiorze nagłówkowym stdio.h ma postać size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream); Funkcja fwrite zapisuje, do pliku określonego przez stream, podaną ilość elementów (n) o jednakowej wielkości (size). Funkcja zwraca ilość zapisanych elementów. Znaczenie argumentów jest następujące: const void *ptr wskaźnik na blok pamięci zawierający dane, które mają być zapisane do pliku przez funkcję fwrite. Słówko const oznacza, że funkcja nie zmienia podczas swojego działania zawartości przekazanego jej bufora. size_t size rozmiar pojedynczego elementu, które będą zapisywane size_t n ilość elementów do zapisania FILE *stream wskaźnik do pliku, do którego będziemy zapisywać Przykład: Zadaniem programu jest przeczytanie z podanego pliku dziesięciu bajtów i wyświetlenie ich na ekran w kodzie hex i w postaci znaków. #include <stdio.h> main( ) { int i, j; char buf[10]; fp = fopen("c:\\plik.txt", "r"); if(fp == NULL) { printf("nie udalo się otworzyc pliku: c:\\plik.txt \n"); return -1; i = fread(buf, 1, 10, fp); j=0; while(i--) { printf("znak %c ma kod %X (hex)\n", buf[j], buf[j]); j++; fclose(fp); return 0; Zadanie 3: Napisać program kopiuj, który skopiuje plik wejściowy na plik wyjściowy. Do rozwiązania zadania użyć funkcji fwrite i fread, które będą pisać/czytać większymi, ustalonymi wcześniej porcjami (np. po
Laboratorium Podstaw Informatyki Strona 5 10 bajtów). Zauważ, że długość kopiowanego pliku nie musi być (i na ogół nie jest) całkowitą wielokrotnością przyjętej porcji czytania. Zastanów się jak rozwiązać ten problem. Popularna funkcja printf ma swój odpowiednik fprintf, który wyprowadza wynik działania nie na ekran lecz do pliku. Przykładowe użycie wyjaśnia zarazem sposób przekazywania parametrów: fprintf(fp, "znak %c ma kod %X (hex)\n", X, X ); oczywiście FILE *fp jest wskaźnikiem do otwartego wcześniej pliku. Zadanie 4: Zmodyfikować tak zadanie 2, aby histogram był wyprowadzany równocześnie do podanego pliku jak i na ekran. W tym celu należy napisać funkcję DrukujHist(int *hist, FILE *fp), w której ciele należy użyć funkcji fprintf. Funkcja DrukujHist będzie w programie wołana 2 raz: raz z parametrem fp wskazującym na plik dyskowy, a drugi raz równym stdout, aby wyświetlić wynik na ekranie. Zadanie 5: Rozszerzyć napisany program z zadania 3, tak aby mógł być użyty do konkatenacji (t.j. sklejania) kilku plików. Nazwy plików do sklejenia będą zadawane w tablicy tekstów, np. char *fn[] = { plik1, plik2, wynik, NULL;. Przy tak zadeklarowanej tablicy fn[0] jest tekstem (tj. numerem komórki pamięci w której umieszczony jest pierwszy znak tekstu) plik1, fn[2] wskazuje na wynik, a fn[3] jest NULL. Widać więc jaki powinien być warunek na zakończenie pętli przeglądającej taką tablicę. Przy takiej zawartości tablicy do pliku wynik najpierw zostanie wpisana zawartość pliku plik1, a potem dopisana zawartość pliku plik2.