Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
1
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
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
2
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.
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
3
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.
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
4
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():
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
5
• 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);
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
6
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.
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
7
#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):
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
8
#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
;
Podstawy programowania. Wykład 15 –wejście /wyjście
Małgorzata Nalbach-Moszyńska
9
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;
}