Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
15. Wejście / wyjście ............................................................................................................ 2
15.1
Informacje ogólne .................................................................................................... 2
15.2
Wyjście standardowe ............................................................................................... 2
15.3
Podstawowa obsługa plików .................................................................................... 2
15.3.1 Otwieranie pliku - fopen ...................................................................................... 3
15.3.2 Zamykanie pliku - fclose ...................................................................................... 3
15.4
Przykłady funkcji obsługi wyjścia ........................................................................... 4
15.4.1 Funkcja printf /fprintf ........................................................................................... 4
15.4.2 Funkcja puts /fputs ............................................................................................... 5
15.4.3 Funkcja putchar/putc ............................................................................................ 6
15.5
Wejście ..................................................................................................................... 6
15.5.1 Wejście formatowane scanf/fscanf ....................................................................... 6
15.5.2 Czytanie po znaku getchar/getc ............................................................................ 7
15.5.3 Czytanie tablicy znaków ...................................................................................... 8
1
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
15.
Wejście / wyjście
15.1 Informacje ogólne
W odróżnieniu od innych języków programowania mechanizmy obsługi wejścia / wyjścia w C i C++ nie są zawarte w standardzie języka – ich obsługą zajmują się funkcje z bibliotek dołączanych na początku programu.
#include <stdio.h>
Biblioteka funkcji wejścia / wyjścia.
15.2 Wyjś cie standardowe
W zasadzie standard C nie definiuje czegoś takiego jak ekran i klawiatura - mowa w nim o standardowym wyjściu i standardowym wejściu. Zazwyczaj jest to właśnie ekran i klawiatura, ale nie zawsze. W szczególności użytkownicy Linuksa lub innych systemów uniksowych mogą być przyzwyczajeniu do przekierowania wejścia/wyjścia z/do pliku czy łączenie komend w potoki (ang. pipe). W takich sytuacjach dane nie są wyświetlane na ekranie, ani odczytywane z klawiatury.
15.3 Podstawowa obsługa plików
Istnieją dwie metody obsługi czytania i pisania do plików:
• wysokopoziomowa,
• niskopoziomowa.
Nazwy funkcji z pierwszej grupy zaczynają się od litery "f" (np. fopen(), fread(), fclose()), a identyfikatorem pliku jest wskaźnik na strukturę typu FILE. Owa struktura to pewna grupa zmiennych, która przechowuje dane o danym pliku - jak na przykład aktualną pozycję w nim.
Szczegółami nie musisz się przejmować, funkcje biblioteki standardowej same zajmują się wykorzystaniem struktury FILE, programista może więc zapomnieć, czym tak naprawdę jest struktura FILE i traktować taką zmienną jako "uchwyt", identyfikator pliku.
Druga grupa to funkcje typu read(), open(), write() i close().
Podstawowym identyfikatorem pliku jest liczba całkowita, która jednoznacznie identyfikuje dany plik w systemie operacyjnym. Liczba ta w systemach typu UNIX jest
nazywana deskryptorem pliku.
Należy pamiętać, że nie wolno nam używać funkcji z obu tych grup jednocześnie w stosunku do jednego, otwartego pliku, tzn. nie można najpierw otworzyć pliku za pomocą fopen(), a następnie odczytywać danych z tego samego pliku za pomocą read().
Czym różnią się oba podejścia do obsługi plików? Otóż metoda wysokopoziomowa ma swój własny bufor, w którym znajdują się dane po odczytaniu z dysku a przed wysłaniem ich do programu użytkownika. W przypadku funkcji niskopoziomowych dane kopiowane są
bezpośrednio z pliku do pamięci programu. W praktyce używanie funkcji
wysokopoziomowych jest prostsze a przy czytaniu danych małymi porcjami również często szybsze.
2
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
15.3.1 Otwieranie pliku - fopen
FILE *fopen(const char *filename, const char *mode);
Opis
Funkcja fopen() otwiera plik, którego nazwa podana jest w pierwszym argumencie. Drugim jest łańcuch znaków zwierający litery oznaczające sposób otwarcia pliku:
• "r" - otwiera plik do czytania
• "r+" - otwiera plik do czytania i nadpisywania (aktualizacja)
• "w" - otwiera plik do nadpisywania (zamazuje starą treść)
• "w+" - otwiera plik do nadpisywania i czytania
• "a" - otwiera plik do dopisywania (jeśli plik nie istnieje, to jest tworzony)
• "a+" - otwiera plik do dopisywania i odczytu (jeśli plik nie istnieje, to jest tworzony)
• "t" - otwiera plik w trybie tekstowym
• "b" - otwiera plik w trybie binarnym
Litery można ze sobą łączyć, np. "rwb" albo "wt".
Wartość zwracana
Wskaźnik do pliku (FILE *) lub NULL, gdy pliku nie udało się otworzyć.
Przykład użycia
#include <stdio.h>
int main()
{
FILE *f = fopen("notatki.txt", "r");
// otwiera plik do odczytu (musi istniec)
if (f == NULL)
{
perror("Nie udalo sie otworzyc pliku notatki.txt");
return 1;
}
puts("Plik otwarty pomyslnie!");
fclose(f);
return 0;
}
15.3.2 Zamykanie pliku - fclose
Deklaracja
int fclose (FILE *plik);
Opis
Funkcja fclose() zamyka otwarty funkcją fopen() plik. Jeśli plik nie zostanie zamknięty, a program zakończy działanie, zmiany nie zostaną zapisane.
3
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
Wartość zwracana
Funkcja zwraca 0, a w przypadku błędu EOF.
Przykład użycia
#include <stdio.h>
int main()
{
FILE *f = fopen("plik.txt", "w");/* otworzenie pliku do zapisu
*/
if (f)
{
fputs("Przyklad z podrecznika C na Wikibooks!", f);
/* zapisanie do pliku */
fclose(f); /* zamknięcie pliku i zapisanie zmian */
}
return 0;
}
15.4 Przykłady funkcji obsługi wyjś cia
15.4.1 Funkcja printf /fprintf
printf(format, argument1, argument2, ...);
fprintf(plik, format, argument1, argument2, ...);
Format to napis ujęty w cudzysłowy, który określa ogólny kształt, schemat tego, co ma być wyświetlone. Format jest drukowany tak, jak go napiszemy, jednak niektóre znaki specjalne zostaną w nim podmienione na co innego.
Przykładowo, znak specjalny \n jest zamieniany na znak nowej linii ( Zmiana ta następuje w momencie kompilacji programu i dotyczy wszystkich literałów napisowych. Nie jest to jakaś szczególna własność funkcji printf(). ).
Natomiast procent jest podmieniany na jeden z argumentów. Po procencie następuje specyfikacja, jak wyświetlić dany argument. W tym przykładzie %i (od int) oznacza, że argument ma być wyświetlony jak liczba całkowita. W związku z tym, że znaki \ i % mają specjalne znaczenie, aby wydrukować je, należy użyć ich podwójnie:
Plik – to wskaźnik do struktury typu FILE – musi być otwarty do czytania przez fopen.
Przykład:
int i = 500;
printf("Liczbami całkowitymi są na przykład %i oraz %i. \n", 1, i); wypisze
Liczbami całkowitymi są na przykład 1 oraz 500.
Najczęstsze użycie printf():
4
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
• printf("%i", i); gdy i jest typu int; zamiast %i można użyć %d
• printf("%f", i); gdy i jest typu float lub double
• printf("%c", i); gdy i jest typu char (i chcemy wydrukować znak)
• printf("%s", i); gdy i jest napisem (typu char*)
15.4.2 Funkcja puts /fputs
int puts(const char *s);
int fputs (const char *s, FILE *stream);
Funkcja wysyła na standardowe wyjście/ plik napis s, a następnie (tylko funkcja puts) znak nowej linii.
Funkcja zwraca liczbę nieujemną w przypadku sukcesu. W przypadku błędu zwraca wartość EOF.
W ten sposób, nasz pierwszy program moglibyśmy napisać w ten sposób:
#include <stdio.h>
int main()
{
puts("Hello world!");
return 0;
}
W swoim działaniu funkcja ta jest w zasadzie identyczna do wywołania:
printf("%s\n", argument);
jednak prawdopodobnie będzie działać szybciej. Jedynym jej mankamentem może być fakt, że zawsze na końcu podawany jest znak przejścia do nowej linii. Jeżeli jest to efekt niepożądany (nie zawsze tak jest) należy skorzystać z funkcji fputs() opisanej niżej lub wywołania printf("%s", argument);
5
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
15.4.3 Funkcja putchar/putc
putchar (char znak);
putc(char znak, FILE *plik);
Funkcje putchar() i putc() służy do wypisywania pojedynczych znaków. Przykładowo, jeżeli chcielibyśmy napisać program wypisujący w prostej tabelce wszystkie liczby od 0 do 99
moglibyśmy to zrobić tak:
#include <stdio.h>
int main(void)
{
int i = 0;
for (; i<100; ++i)
{
/* Nie jest to pierwsza liczba w wierszu */
if (i % 10)
{
putchar(' ');
}
printf("%2d", i);
/* Jest to ostatnia liczba w wierszu */
if ((i % 10)==9)
{
putchar(' \n');
}
}
return 0;
}
15.5 Wejś cie
15.5.1 Wejście formatowane scanf/fscanf
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
Funkcje odczytują dane zgodnie z podanym formatem. Funkcja scanf odczytuje dane ze standardowego wejścia (tj. stdin); fscanf ze strumienia podanego jako argument.
6
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
#include <stdio.h>
int main ()
{
int liczba = 0;
printf ("Podaj liczbę: ");
scanf ("%d", &liczba); /* parametr liczba jest wyjściowy dla scanf */
printf ("%d*%d=%d\n", liczba, liczba, liczba*liczba);
return 0;
}
Funkcja scanf() zwraca liczbę poprawnie wczytanych zmiennych lub EOF, jeżeli nie ma już danych w strumieniu lub nastąpił błąd.
Załóżmy dla przykładu, że chcemy stworzyć program, który odczytuje po kolei liczby i wypisuje ich 3 potęgi. W pewnym momencie dane się kończą lub jest wprowadzana
niepoprawna dana i wówczas nasz program powinien zakończyć działanie. Aby to zrobić, należy sprawdzać wartość zwracaną przez funkcję scanf() w warunku pętli:
#include <stdio.h>
int main(void)
{
int n;
while (scanf("%d", &n)==1)
{
printf("%d\n", n*n*n);
}
return 0;
}
15.5.2 Czytanie po znaku getchar/getc
int getchar(void);
int getc(FILE *file);
Funkcja getchar() / getc czyta znak ze standardowego wejścia/strumienia file i go stamtąd usuwa. Wywołanie getchar() jest równoważne wywołaniu getc(stdin).
W wielu przypadkach dane mogą być buforowane, przez co wysyłane są do programu
dopiero, gdy bufor zostaje przepełniony lub na wejściu jest znak przejścia do nowej linii. Z
tego powodu po wpisaniu danego znaku należy nacisnąć klawisz enter, aczkolwiek trzeba pamiętać, że w następnym wywołaniu zostanie zwrócony znak przejścia do nowej linii. Gdy nastąpił błąd lub nie ma już więcej danych funkcja zwraca wartość EOF (która ma jednak wartość logiczną 1 toteż zwykła pętla while (getchar()) nie da oczekiwanego rezultatu): 7
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
#include <stdio.h>
int main(void)
{
int c;
while ((c = getchar())!=EOF) {
if (c==' ') {
c = '_';
}
putchar(c);
}
return 0;
}
Ten program wczytuje dane znak po znaku i zamienia wszystkie spacje na znaki podkreślenia.
Może wydać się dziwne, że zmienną c zdefiniowaliśmy jako trzymającą typ int, a nie char.
Właśnie taki typ (tj. int) zwraca funkcja getchar() i jest to konieczne ponieważ wartość EOF
wykracza poza zakres wartości typu char (gdyby tak nie było to nie byłoby możliwości rozróżnienia wartości EOF od poprawnie wczytanego znaku).
15.5.3 Czytanie tablicy znaków
char *fgets(char *str, int size, FILE *stream);
Opis
Funkcja fgets() czyta kolejne znaki ze strumienia stream i umieszcza je w tablicy znakowej wskazywanej przez str. Czytanie przerywa, gdy przeczyta size - 1 znaków, natrafi na koniec pliku lub znak końca linii (znak ten jest zapisywany do str). Na końcu fgets() dopisuje znak
'\0'.
Istnieje funkcja gets, ale nie sprawdza rozmiaru bufora. Nie wolno jej używać!!
Wartość zwracana
Wartością funkcji fgets(str, size, stream) jest str w przypadku sukcesu. W przypadku błędu lub natrafienia na koniec pliku przed przeczytaniem jakiegokolwiek znaku wartością funkcji jest NULL.
Uwagi
Funkcja fgets() nie odróżnia sytuacji osiągnięcia końca pliku od błędu odczytu pliku. Jeśli potrzebne jest ich rozróżnienie użyj funkcji feof() lub ferror().
Przykład użycia
#include <stdio.h>
int main()
{
int i;
const int max_n= 50;
8
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
const int linie= 2;
char napis[max_n+1], *result;
for (i = 1; i <= linie; ++i)
{
result = fgets (napis, max_n, stdin);
/* czytamy ze standardowego wejś cia */
if (result != NULL)
{
printf ("%d %s", i, napis);
if (feof (stdin))
printf (" \n%d koniec pliku\n", i);
}
else
printf (" \n%d blad odczytu\n", i);
}
return 0;
}
Program wczytuje 2 linie i, w przypadku wystąpienia, sygnalizuje koniec pliku lub błąd odczytu.
Napisz program, który wczytuje dane z pliku tekstowego wierszami i wyświetla je na ekranie.
Do przechowywania napisów zastosuj tablice typu char.
Przykładowe rozwiązanie:
#include <stdio.h>
const int DLWIERSZA = 80; /* 80 znakow w wierszu */
/* program bedzie dzielil dluzsze wiersze */
int main() {
char nazwaPliku[40];
FILE *f;
char wiersz[DLWIERSZA+1]; /* +1 dla konca napisu */
printf( "Nazwa pliku: ");
scanf("%s", &nazwaPliku);
printf("Otwieram plik: %s\n",nazwaPliku );
f = fopen(nazwaPliku, "r"); /* Otworz plik tekstowy do czytania */
if (!f) {
fprintf(stderr, "Nie moge otworzyc %s.\n", nazwaPliku);
return 1;
}
/* Wczytuj wiersze do napotkania konca pliku. */
while (fgets(wiersz, DLWIERSZA+1,f)) {
printf( "%s", wiersz);
}
printf( "*** KONIEC PLIKU ***\n");
return 0;
}
9