• Nie Znaleziono Wyników

Strumienie mogą być otwierane w trybie:

N/A
N/A
Protected

Academic year: 2021

Share "Strumienie mogą być otwierane w trybie: "

Copied!
41
0
0

Pełen tekst

(1)

Pliki w C++

(2)

• Plik jest reprezentowany przez strumień znaków (bajtów) o zmiennej długości.

• Koniec strumienia identyfikowany jest znacznikiem końca pliku — EOF.

• Z każdym strumieniem związany jest wskaźnik bieżącej pozycji — od tej pozycji realizowane będzie czytanie lub pisanie. Każdy zapis i odczyt zmienia wskaźnik bieżącej pozycji.

• Z każdym strumieniem związany jest znacznik osiągnięcia końca pliku oraz znacznik błędu.

(3)

Strumienie mogą być otwierane w trybie:

• Binarnym — strumień jest ciągiem jednakowo traktowanych bajtów, każdy zapis i odczyt realizowany jest bez żadnych konwersji.

• Tekstowym — strumień jest ciągiem linii tekstu zakończonych znacznikiem końca linii — znak ’\n’.

W trakcie odczytu i zapisu do takiego strumienia mogą zachodzić konwersje spowodowane np.

różna fizyczna reprezentacja znacznika końca wiersza (np. para \r\n w plikach tekstowych DOS/Windows, pojedynczy znak \n w systemach Unix’owych).

(4)

Otwieranie plików

• Aby rozpocząć operacje na plikach należy zadeklarować w programie zmienną stanowiącą „dojście” do takiego pliku.

• W przypadku obsługi standardowych strumieni deklaruje się zmienną wskaźnikową.

• Typem wskazywanym jest FILE, jest to zdefiniowany w pliku nagłówkowym stdio.h typ rekordowy, zawierający informacje o otwartym dojściu do pliku.

• Wykorzystanie pliku rozpoczyna operacja jego otwarcia, realizowana zwykle przez funkcję fopen, otwarcie pliku dane.txt do odczytu w trybie tekstowym może wyglądać następujaco:

(5)

fopen

Pierwszy parametr funkcji fopen to nazwa pliku (być może ze ścieżką), drugi parametr to napis zawierający tryb otwarcia – r oznacza odczyt, t tryb tekstowy.

W przypadku poprawnego otwarcia pliku, funkcja fopen alokuje na stercie rekord opisu pliku (typu FILE), wypełnia go odpowiednimi informacjami i oddaje w postaci rezultatu wskaźnik do tego rekordu. Wskaźnik ten stanowi odtąd „dojście” do pliku i będzie przekazywany każdej funkcji operującej na pliku, informując ją, na jakim pliku ma wykonywać właściwe dla niej operacje.

Zatem w przypadku poprawnego otwarcia pliku wskaźnik fp będzie miała wartość różną od NULL. Wtedy można wykonywać dalsze operacje na pliku. Jeżeli w trakcie otwierania pliku nastąpił błąd, rezultatem funkcji fopen jest wartość NULL. Nie można wtedy przetwarzać pliku.

(6)

FILE * fopen( const char *filename, const char *mode );

• Otwiera strumień związany z plikiem o nazwie przekazanej parametrem filename.

• Nazwa może zawierać ścieżkę dostępu do pliku.

Strumień otwierany jest w trybie mode.

Jeżeli otwarcie zakończyło się sukcesem, funkcja udostępnia wskaźnik do dynamicznie alokowanej struktury typu FILE, stanowiącej programową reprezentację fizycznego pliku.

• Jeżeli pliku nie udało się otworzyć, rezultatem funkcji jest NULL.

(7)
(8)

Znak + w trybie otwarcia oznacza aktualizacje — możliwość czytania i pisania do otwartego strumienia. Jednak zapis odczyt i zapis (albo zapis i odczyt) nie mogą po sobie następować bezpośrednio. Należy użyć funkcji

„wymiatania” bufora fflush lub jednej z funkcji pozycjonowania pozycji — fseek, fsetpos, rewind.

Jeżeli informacja o trybie otwarcia (t lub b) nie występuje, przyjmowany jest tryb otwarcia zgodnie z wartością globalnej zmiennej _fmode. Jeżeli _fmode posiada wartość O_BINARY, plik jest otwierany w trybie binarnym, jeżeli _fmode posiada wartość O_TEXT, plik jest otwierany w trybie tekstowym.

Domyślna wartość to O_TEXT. Symbole O_... są zdefiniowane w pliku fcntl.h.

(9)

Zamykanie plików

Do zamykania pliku służy funkcja fclose.

Przykład jej wykorzystania przedstawiono poniżej.

if( ( fp = fopen( "dane.dat", "rt" ) ) != NULL ) {

/* Otwarcie OK, wykonaj operacje na pliku */

. . .

/* Zamknij plik jeżeli przetwarzanie zakończono */

fclose( fp );

}

else {

/* Otwarcie nieudane, obsługa sytuacji błędnej */

. . . }

(10)

• Dokładniejszy opis funkcji zamykającej plik jest następujący:

int fclose( FILE * stream );

• Funkcja zamyka strumień i zapisuje wszystkie bufory.

• Rezultat EOF oznacza błąd zamykania, rezultat równy zero oznacza bezbłędne zamknięcie.

• Pamięć przydzielona strukturze wskazywanej przez wskaźnik stream jest zwalniana.

(11)

Odczyt i zapis pojedynczych znaków

• Odczyt pojedynczego znaku ze strumienia realizuje funkcja fgetc:

int fgetc( FILE *stream );

• Funkcja pobiera następny znak ze strumienia identyfikowanego przez stream i uaktualnia wskaźnik bieżącej pozycji w pliku.

• Znak pobierany jest jako unsigned char i przekształcany jest do typu int.

• W przypadku napotkania końca strumienia rezultatem jest wartość EOF oraz ustawiany jest znacznik napotkania końca strumienia.

• W przypadku wystąpienia błędu odczytu rezultatem funkcji jest wartość EOF oraz ustawiany jest znacznik błędu strumienia.

(12)

• Zapis pojedynczego znaku do strumienia realizuje funkcja fputc:

int fputc(int c, FILE *stream);

• Funkcja wyprowadza znak c do strumienia stream zgodnie ze wskaźnikiem bieżącej pozycji w pliku.

• W przypadku, gdy funkcja fputc zakończyła swoje działanie bez błędu, rezultatem funkcji jest znak c. W przeciwnym wypadku wartość EOF.

(13)

Przy sekwencyjnym przetwarzaniu plików istotna jest umiejętność testowania np. czy w wyniku ostatniej operacji odczytu napotkano koniec pliku. Służyć do tego może funkcja feof:

int feof( FILE * stream );

Rezultatem funkcji jest wartość różna od zera jeżeli strumień jest w pozycji końcowej, zero w przeciwnym wypadku. Strumień jest w pozycji końcowej, jeżeli w wyniku ostatnio przeprowadzonej operacji odczytano znacznik końca pliku.

Uwaga – funkcja feof działa inaczej niż jej odpowiednik z języka Pascal – funkcja Eof. Rezultatem funkcji Eof jest wartość True, jeżeli przeczytano ostatni element w pliku. Rezultatem funkcji feof jest wartość różna od zera, jeżeli próbujemy czytać po odczytaniu znacznika końca pliku.

Powoduje to pewne problemy…

(14)

przykład

Załóżmy, że plik d.txt ma następującą zawartość:

123456789

(15)
(16)

efekt

Jak widać program doliczył do rzeczywistej

zawartości pliku również znacznik jego końca.

Aby tak nie było, można zaproponować inną

sekwencję przetwarzania pliku:

(17)

Poprawniej jest…

(18)

• Kluczowa jest tutaj sekwencja stanowiąca warunek iteracji while.

• Funkcja fgetc pobiera znak z pliku, jest on zapamiętywany w zmiennej c a następnie porównywany ze stała EOF.

• W przypadku odczytania znacznika końca

pliku, trafia on do zmiennej c, lecz iteracja

jest zrywana i program prawidłowo zadziała –

wykaże 9 znaków w pliku d.txt.

(19)

Spróbujmy wykorzystać podobna sekwencje dla wyznaczenia rozmiaru pliku liczonego w bajtach, tym razem wykorzystajmy iteracje for:

if( ( fp = fopen( "d.txt", "rt" ) ) != NULL ) {

for( counter = 0; fgetc( fp ) != EOF; counter++ )

;

fclose( fp );

printf( "\nRozmiar pliku: %ld bajtow", counter );

}

Niestety weryfikacja działania tego programu pokaze, iz działa on błednie, zobacz Rysunek 3.

(20)
(21)

Przyczyną wadliwego działania programu są konwersje znaczników końca linii w trybie tekstowym.

W systemach DOS/Windows znacznik końca linii to para \r i \n (czyli CR i LF). W trakcie odczytu w trybie tekstowym, każda para \r\n zamieniana jest na pojedynczy znak \n.

Analogicznie jest w przypadku zapisu – pojedynczy znacznik \n zapisywany jest do pliku fizycznie w postaci pary \r\n.

(22)

Konwersja znaczników końca wiersza w

trybie tekstowym

(23)

Konwersje takie nie zachodzą przy otwieraniu pliku w trybie binarnym.

W drugim parametrze wywołania należy zatem

użyć litery b, oznaczającej otwarcie w trybie

binarnym.

(24)

poprawiona wersja kodu, ujęta w ramy funkcji file_size

long int file_size( char * fname ) {

FILE * fp;

long int counter = 0;

if( ( fp = fopen( fname, "rb" ) ) != NULL ) {

for( counter = 0; fgetc( fp ) != EOF; counter++ )

; fclose( fp );

}

return counter;

} . . .

printf( "\nRozmiar pliku: %ld bajtow", file_size( "d.txt" ) );

(25)
(26)

Kopiowanie jednego pliku do innego…

(27)

Kopiowanie z zamianą znaków na duże

znaki…

(28)

Co jeszcze ?

1. W trakcie kopiowania realizuj filtrowanie

znaków, np. kopiowanie tylko liter i cyfr

2. Kopiowanie wyłącznie dużych liter

(29)

Ćwiczenia w przetwarzaniu plików znak po znaku

1. Napisać program wyznaczający ile w pliku tekstowym jest liter dużych, małych, cyfr oraz znaków interpunkcyjnych (znaki kropki, przecinka, średnika). Program pyta użytkownika o nazwę pliku, dla którego ma przeprowadzić powyższe statystyki.

2. Napisać program wyznaczający liczbę wierszy w pliku tekstowym. Program pyta użytkownika o nazwę pliku, dla którego ma przeprowadzić ta operacje. Wskazówka – każdy wiersz w pliku tekstowym (nawet pusty) kończony jest znakiem \n.

3. Napisać program wyznaczający współczynnik skomentowania programu w jezyku C, tzn. procentowy stosunek liczby znaków w komentarzach do ogólnej liczby znaków w programie. Program pyta użytkownika o nazwę pliku, dla którego ma przeprowadzić tą operację. W statystyce nie biorą udziału tzw. „białe znaki”, czyli znaki spacji, tabulacji i nowego wiersza.

4. Napisać program kopiowania zawartości jednego pliku do drugiego. Program powinien zapytać o nazwy pliku źródłowego i docelowego. W trakcie kopiowania każda duża litera ma być zamieniana na małą, a każda mała na dużą.

5. Napisać program wyznaczający liczbę słów zawartych w pliku tekstowym. Jako słowo traktowane ma być dowolny ciąg liter, cyfr i znaków podkreślenia, rozpoczynający się od litery. Program pyta użytkownika o nazwę pliku wejściowego.

(30)

Przetwarzanie plików liniami

• Pliki tekstowe reprezentowane są również jako strumienie bajtów.

• Można je jednak przetwarzać wierszami, od strony programu separatorem wierszy jest znak \n.

• Do przetwarzania pliku tekstowego linia po

linii służą funkcje odczytu/zapisu linii —

buforem linii są tablice znakowe.

(31)

Pliki tekstowe reprezentowane są również jako strumienie bajtów. Można je jednak przetwarzać wierszami, od strony programu separatorem wierszy jest znak \n.

Do przetwarzania pliku tekstowego linia po linii służą funkcje odczytu/zapisu linii — buforem linii są tablice znakowe.

Odczyt linii tekstu z pliku realizuje znana już funkcja fgets, zapis np. fputs, fprintf .

int fputs( const char * s, FILE * stream );

Funkcja fputs wyprowadza napis s do pliku stream, nie dopisuje znaczników końca wiersza ani końca napisu.

Rezultatem funkcji jest ostatni zapisany znak, w przypadku gdy zapis zakończył się sukcesem lub EOF, gdy wystąpił błąd.

(32)

Funkcja fprintf

int fprintf( FILE * stream, const char * format [, argument, ...] );

Funkcja fprintf wyprowadza do pliku stream napis format oraz opcjonalne argumenty, w postaci określonej przez sekwencje formatujące zapisane w napisie format.

Rezultatem funkcji jest liczba wyprowadzonych bajtów, w przypadku gdy zapis zakończył się sukcesem lub EOF, gdy wystąpił błąd..

Wszystkie zasady formatowania znane z wykorzystania funkcji printf obowiązują dla funkcji fprintf.

Zamiast wywołania funkcji printf:

fprintf( stdout, "printf to fprintf piszacy do stdout" );

(33)
(34)

Funkcja fgets

Pierwszy parametr s określa bufor, do którego mają być zapisane wczytywane dane.

Drugi parametr n określa maksymalną pojemność bufora, uwzględniającą miejsce na znacznik końca napisu.

Trzeci parametr stream określa strumień (plik), z którego funkcja ma odczytywać dane, może to być również standardowy strumień wejściowy ― stdin.

Działanie funkcji kończy się gdy funkcja odczyta n – 1 znaków lub wcześniej zostanie odczytany znak nowego wiersza (Enter).

Znacznik końca napisu dopisywany jest na jego końcu.

Funkcja fgets pozostawia w buforze znacznik końca wiersza

(35)
(36)

Wyprowadza do stdout zawartość pliku

o nazwie fname.

(37)

ćwiczenia w przetwarzaniu plików linia po linii

Napisać program fview - przeglądacz plików tekstowych. Będzie wyświetlać na ekranie zawartość pliku tekstowego przekazanego parametrem wywołania programu. Jeżeli program zostanie wywołany w linii poleceń:

fview ala.txt

to ma wyświetlić zawartość pliku ala.txt lub sensownie zareagować na przypadek gdy taki plik nie istnieje. Program może być również wywoływany z opcjami:

fview -p ala.txt

gdzie –p (od ang. page) ma powodować wyświetlanie ze stronicowaniem, tzn. z zatrzymaniem co 25 linii

fview -c ala.txt

gdzie –c (od ang. cut) ma powodować obcinanie linii do 80 znaków, tzn. linie dłuższe niż 80 znaków nie będą zawijane do następnej linii ekranowej.

fview -u ala.txt

gdzie –u (od ang. upper) ma powodować wyświetlanie zawartości pliku dużymi literami.

fview -l ala.txt

gdzie –l (od ang. lower) ma powodować wyświetlanie zawartości pliku małymi literami. Opcje mogą być łączone. tzn. jednocześnie -p i -u. Przykładowo wywołanie:

fview –upc ala.txt

Ma spowodować wyświetlenie zawartości pliku tekstowego dużymi literami, ze stronicowaniem i z obcinaniem zbyt długich linii. Oczywiście jednoczesne stosowanie opcji –u i –l sie wyklucza.

(38)

Wskazówki…

#define MAX_LINE 256

#define MAX_CHARS_IN_LINE 80 void list_file_nowrap( char * fname ) {

FILE * fp;

char buffer[ MAX_LINE ];

if( ( fp = fopen( fname, "rt" ) ) != NULL ) {

while( fgets( buffer, MAX_LINE, fp ) != NULL ) {

if( strlen( buffer ) > MAX_CHARS_IN_LINE ) {

buffer[ MAX_CHARS_IN_LINE - 1 ] = '>';

buffer[ MAX_CHARS_IN_LINE ] = '\0';

}

printf( "%s", buffer );

}

fclose( fp );

} }

funkcja list_file nie łamie za długich wierszy a umieszcza na ich końcu symbol informujący, że wiersz jest dłuższy od szerokości ekranu.

Realizuje to funkcja list_file_nowrap o następującej postaci:

Stała MAX_CHARS_IN_LINE reprezentuje aktualną szerokość okna konsoli liczoną w znakach. Linia zapisana w tablicy buffer jest obcinana do tej liczby znaków poprzez wstawienie na pozycji MAX_CHARS_IN_LINE znacznika końca napisu. Na znaku poprzednim wstawiany jest symbol '>' sugerujący użytkownikowi, że linia jest dłuższa niż szerokość okna konsoli.

(39)

Numerowanie wierszy

#define MAX_LINE 256

#define MAX_CHARS_IN_LINE 80 void nlist_file_nowrap( char * fname ) {

FILE * fp;

int counter = 0;

char buffer[ MAX_LINE ];

if( ( fp = fopen( fname, "rt" ) ) != NULL ) {

while( fgets( buffer, MAX_LINE, fp ) != NULL ) {

if( strlen( buffer ) > MAX_CHARS_IN_LINE - 5 ) {buffer[ MAX_CHARS_IN_LINE - 6 ] = '>';

buffer[ MAX_CHARS_IN_LINE - 5 ] = '\0';

}

printf( "%03d: %s", ++counter, buffer );

}

fclose( fp );

} }

Numerowanie linii opiera sie na zmiennej counter, dla numerów linii zarezerwowano domyślnie trzy cyfry. łącznie ze znakiem dwukropka i jedna spacja

daje to 5 znaków. O tyle mniej znaków można zmieścić w linii tekstu, stąd inne niż poprzednio wartości przy obcinaniu zbyt długich linii.

(40)

poszukiwanie linii zawierających określony wzorzec tekstowy. Realizuje to przedstawiona dalej funkcja pattern_list_file. Otrzymuje ona w postaci drugiego parametru wywołania tablice znaków pattern, zawierająca poszukiwany wzorzec.

#define MAX_LINE 256

#define MAX_CHARS_IN_LINE 80

void pattern_list_file( char * fname, char * pattern ) {

FILE * fp;

int counter = 0;

char buffer[ MAX_LINE ];

if( ( fp = fopen( fname, "rt" ) ) != NULL ) {

while( fgets( buffer, MAX_LINE - 1, fp ) != NULL ) {

++counter;

if( strstr( buffer, pattern ) != NULL ) {

if( strlen( buffer ) > MAX_CHARS_IN_LINE - 5 ) {

buffer[ MAX_CHARS_IN_LINE - 6 ] = '>';

buffer[ MAX_CHARS_IN_LINE - 5 ] = '\0';

}

printf( "%03ld: %s", counter, buffer );

} }

fclose( fp );

} }

(41)

• Dla tej funkcji kluczowe jest wywołanie funkcji strstr, przeszukującej tablice buffer w poszukiwaniu podciągu identycznego z pattern.

W przypadku znalezienia takiego podciągu rezultatem funkcji jest wskaźnik na początek podciągu znalezionego w tablicy buffer.

• W naszym przypadku sam fakt znalezienia podciągu (a więc niezerowy rezultat funkcji strstr) wystarcza, by zakwalifikować linie do wyświetlenia.

Cytaty

Powiązane dokumenty

Jeśli trening jest zbyt krótki lub/i liczba neuronów zbyt mała sieć będzie niedouczona (duże błędy), zbyt długi trening lub/i zbyt duża liczba neuronów skutkuje

• by zapisać znak do pliku wskazywanego przez plik należy użyć funkcji int putc(int znak, FILE *plik), która zwraca wartość tego znaku lub EOF jako sygnał wystąpienia

• by zapisać znak do pliku wskazywanego przez plik należy użyć funkcji int putc(int znak, FILE *plik), która zwraca wartość tego znaku lub EOF jako sygnał wystąpienia

 znak „>” umożliwia przekierowanie strumienia danych ze standardowego wyjścia do pliku; jeżeli plik istnieje, to jego poprzednia zawartość zostaje usunięta, np5. ls

dzimy więc, że czynności prawne, należące do dziedziny prawa materialnego mogą być wciągnięte w ramy procesu i stanowić treść oświadczeń procesowych. Tu powstaje

Katedra Technologii Informatycznych w Inżynierii Wydział Inżynierii Lądowej Politechniki Krakowskiej. Strona

Przykładem klasy dekoracyjnej jest DataInputStream umoŜliwiająca odczyt ze strumienia danych wszystkich podstawowych typów (readByte(), readFloat() ...) i.. stringów.

umowy z Gminą Baranów” powinno być brzmienie „Zwalnia się od podatku od nieruchomości grunty i budynki wykorzystywane na potrzeby systemu