Wprowadzenie
Każdorazowe wprowadzanie danych do utworzonego programu jest użyteczne, jednak w większości przypadków jest to po prostu niepraktyczne. Każda komercyjna gra jak i każda aplikacja korzysta z możliwości odczytywania i zapisywania danych. Nawet w najprostszych grach wykorzystuje się pliki do zapamiętywania najlepszych osiągniętych wyników bądź do zapisywania konfiguracji klawiszy, umożliwiających sterowanie grą. W niniejszym rozdziale nauczysz się w jaki sposób można odczytywać tekst umieszczony w pliku tekstowym.
Dostępne narzędzia do obsługi plików
Dostęp do plików można uzyskać na wiele sposobów. Jeżeli programujesz pod Windowsem, możesz użyć funkcji należących do biblioteki WinAPI. Jeżeli nie chcesz uzależniać się od systemu operacyjnego, możesz użyć funkcji wywodzących się ze standardu C, bądź skorzystać z pakietu narzędzi jakie pojawiły się wraz ze standardem C++. Czego używać najlepiej? Trudno powiedzieć. Programiści wytwarzający aplikacje tylko i wyłącznie pod Windowsa korzystają zazwyczaj z niskopoziomowych funkcji WinAPI. W wielu programach pisanych pod Linuksa wykorzystywane są funkcje dostarczone wraz ze standardem C, co wynika w dużej mierze z zaszłości historycznych. Narzędzia, które pojawiły się wraz ze standardem C++ są jednak całkiem wygodne i również mają swoje grono zwolenników. Ponieważ niniejszy kurs jest poświęcony nauce C++ to rozdział ten będzie omawiał obsługę plików w oparciu o narzędzia dostarczone wraz ze standardem C++.
Biblioteka do obsługi plików
Po całkiem długim wprowadzeniu, czas przejść do omawiania obsługi plików. Aby mieć możliwość korzystania z narzędzi do obsługi plików, dostarczonych wraz ze standardem C++, należy na początku kodu źródłowego dołączyć odpowiedni plik nagłówkowy. Jeżeli w naszej aplikacji będziemy chcieli odczytywać lub zapisywać pliki to wystarczy, że dołączymy plik nagłówkowy
fstream, czyli:
Nazwa pliku jest łatwa do zapamiętania, ponieważ jest to skrót z angielskiego tj. od
File
Stream, co po przetłumaczeniu na nasz język oznacza
strumień plików.
Uchwyt do obsługi pliku
Kolejnym etapem jest utworzenie obiektu, który umożliwi nam komunikację ze wskazanym przez nas plikiem. Obiekt ten tworzy się analogicznie do zmiennych. Typem danych będzie teraz
std::ifstream, natomiast nazwa zmiennej może być dowolna, przykładowo:
Teraz za pomocą zmiennej
plik możemy komunikować się z dowolnym plikiem znajdującym się na dysku. Należy mieć jednak świadomość, że klasa
std::ifstream posiada tylko i wyłącznie metody umożliwiające odczytywanie zawartości pliku, co akurat jest dla Ciebie w chwili obecnej bardzo korzystne, ponieważ nie nadpiszesz sobie niechcący zawartości odczytywanego pliku i jednocześnie będziesz mógł się oswoić z jego obsługą. Warto również wiedzieć, że zmienna za pomocą której można dostać się do danych pliku zazwyczaj nazywana jest
uchwytem do pliku. W przypadku obsługi plików przy pomocy funkcji należących do standardu C (lub funkcji biblioteki WinAPI) nazwa
uchwyt do pliku jest jak najbardziej poprawna. W przypadku stosowania standardu C++ jest to lekkim nadużyciem, bowiem zmienna
plik jest w rzeczywistości obiektem, wewnątrz którego znajduje się uchwyt do pliku. Nie do końca to rozumiesz? Obecnie nie musisz :) Gdy zaczniesz się uczyć programowania obiektowego to wszystko stanie się jasne. W każdym razie jeżeli zmienna typu
std::ifstream zostanie nazwana uchwytem do pliku to należy traktować to jako duży skrót myślowy, ułatwiający komunikację, a nie jako precyzyjne sformułowanie.
Otwieranie wybranego pliku
Mając utworzony już 'uchwyt' do pliku, wystarczy teraz wskazać jaki plik na dysku chcielibyśmy otworzyć do odczytu. Czynność tą wykonuje się przy pomocy metody
open, gdzie pierwszym argumentem metody jest ścieżka do pliku, który chcielibyśmy otworzyć. Załóżmy więc, że mamy na dysku
C:\ plik, który nosi nazwę "odczyt.txt". Otworzenie wspomnianego pliku sprowadzi się więc do następujących linijek:
std::ifstream plik;
plik.open( "C:\\odczyt.txt" );
Jeżeli wspomniany plik będzie istniał na dysku oraz nie będzie on zablokowany do odczytu przez inną aplikację, to wówczas otwarcie pliku zakończy się powodzeniem.
Ścieżka do pliku
Stosunkowo nudną, ale bardzo cenną informacją jest znajomość prawidłowego zapisu ścieżek do pliku w językach C i C++ oraz zdobycie wiedzy, w jaki sposób są obsługiwane ścieżki przez system operacyjny.
Ścieżki względne i ścieżki bezwzględne
Ścieżki do pliku mogą być względne oraz bezwzględne. Ścieżka względna to taka, która
nie zawiera pełnej ścieżki do pliku, tj. nie rozpoczyna się ona od litery (czy też nazwy) dysku. Tym samym ścieżką względną będzie np. podanie samej nazwy pliku (wraz z jego rozszerzeniem!), bądź podanie ścieżki względem
katalogu roboczego aplikacji. Ścieżka bezwzględna określa natomiast pełną ścieżkę do pliku i zaczyna się od litery (nazwy) dysku, a kończy się na pełnej nazwie pliku.
Informacja Stwierdzenie 'litera dysku' dotyczy systemu Windows, natomiast stwierdzenie 'nazwa dysku' dotyczy sytemu Linuks.
|
Katalog roboczy
Katalog roboczy jest to katalog od którego rozpoczyna się poszukiwanie pliku na dysku w przypadku, gdy podana ścieżka do pliku jest
ścieżką względną. Katalogiem roboczym
zazwyczaj jest katalog, w którym znajduje się uruchamiany plik *.exe. Katalog roboczy może być jednak inny, niż katalog w którym znajduje się plik *.exe, co
bardzo często uprzykrza życie początkującym programistom, którzy stwierdzają, że:
'plik jest na dysku, ścieżka względna jest podana poprawnie, a pliku otworzyć się nie da'. Należy więc mieć świadomość, że katalog roboczy może być inny niż katalog, w którym znajduje się plik *.exe w sytuacji, gdy:
Ostatni przypadek dotyczy programistów działających świadomie, natomiast cała reszta jest źródłem wielu problemów dla osób posiadających niewielką wiedzę z zakresu działania różnych mechanizmów w systemach operacyjnych.
Ścieżka do pliku, a znak backslash '\\'
Jak już zapewne zauważyłeś, podczas podawania ścieżki do pliku został użyty dwukrotnie znak backslasha (czyli znak
\ ). Podając znaki, bądź łańcuchy znaków w języku C++ należy pamiętać, że backslash jest znakiem specjalnym, który umożliwia łatwe wstawienie chociażby znaku nowej linii. Tym samym zapisanie pojedynczego backslasha do zmiennej tekstowej (bądź do zmiennej znakowej) wymaga napisania dwóch znaków '\\'. Początkowi programiści prawie zawsze o tym zapominają, a potem godzinami wpatrują się w kod źródłowy szukając błędu wszędzie, tylko nie w podanej ścieżce do pliku. Wniosek?
Zapamiętaj ten fakt! :)
Slashe i backslashe w ścieżce do pliku
Innym, znacznie lepszym rozwiązaniem jest podawanie w ścieżkach slashy (znak /) zamiast backslashy (znak \). Slashe jak również backslashe są traktowane jako znaki równoważne
w ścieżkach do plików, ale dla programistów C++ znak slasha jest znacznie wygodniejszy, bowiem nie jest on znakiem specjalnym, a zatem nie trzeba pamiętać o wspomnianym wcześniej technicznym szczególe i tym samym dużo trudniej popełnić ewentualny błąd przy podawaniu ścieżki.
Ścieżki do plików - podsumowanie
Podając ścieżki do plików warto używać slashy zamiast backslashy. Pamiętaj również, że jeżeli nie możesz otworzyć pliku podając ścieżkę względną, winę może ponosić ustawiony katalog roboczy - wówczas warto spróbować podać ścieżkę bezwzględną w aplikacji, aby mieć pewność, że problem faktycznie dotyczył tylko i wyłącznie właściwego ustawienia katalogu roboczego.
Sprawdzenie czy udało się otworzyć plik
Powróćmy teraz do właściwej nauki obsługi plików. Otworzenie pliku może zakończyć się zarówno powodzeniem jak i fiaskiem. Czynników wpływających na sukces bądź porażkę otwarcia pliku jest wbrew pozorom wiele, a więc zanim zaczniesz wykonywać jakiekolwiek operacje na pliku, warto upewnić się, że faktycznie udało się uzyskać do niego dostęp (czyli udało się otworzyć plik). Do tego celu używa się zazwyczaj metody
good, należącej do klasy
std::ifstream. Linijka za pomocą której możemy sprawdzić czy udało się otworzyć plik, może wyglądać następująco:
if( plik.good() )
{
} else
{
}
Odczytywanie tekstu z pliku
Zaletą narzędzi C++ jest niewątpliwie łatwość ich używania. Odczytywanie tekstu z pliku sprowadza się bowiem do użycia strumienia
>>, bądź zastosowania znanej już Ci funkcji
std::getline, która została omówiona w rozdziale
Wczytywanie tekstu - standardowy strumień wejścia. W związku z tym, że zasada działania wspomnianej funkcji została już omówiona we wspomnianym wyżej rozdziale, to jej zachowanie nie będzie ponownie omawiane. Zamiast tego omówię jak należy używać funkcji
std::getline do pracy z otwartym plikiem:
std::string odczytanyTekst
std::getline( plik, odczytanyTekst );
Powyższy zapis wczytuje jeden wiersz z pliku, którego 'uchwyt' został podany poprzez pierwszy argument omawianej funkcji, natomiast wczytana treść zostanie zapisana do zmiennej
odczytanyTekst. W tym miejscu warto również wspomnieć, że wywołanie powyższego polecenia nie gwarantuje nam odczytania tekstu. Wczytanie kolejnego wiersza tekstu zakończy się niepowodzeniem, gdy w pliku nie będzie więcej tekstu do odczytania. Odczyt może zakończyć się również niepowodzeniem w wyniku innych czynników, takich jak np. awaria urządzenia (np. wysunięto płytę CD, z której odczytywaliśmy zawartość pliku). W dużym uproszczeniu możesz przyjąć, że funkcja std::getline zwraca wartość logiczną
true w przypadku sukcesu, natomiast
false w przypadku niepowodzenia. Tym samym, możesz napisać odczytywanie zawartości całego pliku w następujący sposób:
std::string wiersz;
while( std::getline( plik, wiersz ) )
std::cout << wiersz << std::endl;
Inny sposób odczytania zawartości całego pliku został zawarty w przykładzie, który znajduje się w dalszej części niniejszego rozdziału.
Uwaga! Funkcja std::getline w rzeczywistości nie zwraca wartości logicznej. Rzeczywisty mechanizm, który jest odpowiedzialny za zwracaną wartość wspomnianej funkcji jest jednak na tyle trudny do omówienia w jasny sposób, że na obecnym poziomie Twojej wiedzy lepiej pozostać przy zastosowanym w treści uproszczeniu.
|
Inne metody odczytywania zawartości pliku tekstowego
Inne metody odczytywania zawartości pliku tekstowego zostaną omówione w kolejnym rozdziale, w tym również wspomniany odczyt tekstu za pomocą strumienia
>>.
Zamykanie otwartego pliku
Skoro już wiesz jak otworzyć plik oraz odczytać z niego informacje, wypadałby również wiedzieć w jaki sposób należy go zamknąć. Wiedz, że każdy otwarty plik należy zamykać zaraz po zakończeniu z nim pracy. Do tego celu służy metoda
close, której wywołanie wyglądać może następująco:
Przykład
A teraz część praktyczna :) Przykład prezentujący w jaki sposób odczytuje się zawartość pliku tekstowego został zaprezentowany poniżej. Do prowadzenia eksperymentów z odczytywaniem pliku warto mieć przygotowany jakiś plik tekstowy. Żeby Ci ułatwić naukę, utworzyłem krótki plik tekstowy, który możesz teraz pobrać i zapisać w stosownym katalogu na dysku (plik:
cpp0x.txt).
#include <iostream>
#include <fstream>
#include <string>
bool wyswietlZawartoscPliku( std::string sNazwaPliku )
{
std::ifstream plik;
plik.open( sNazwaPliku.c_str() );
if( !plik.good() )
return false;
std::string wiersz;
while( std::getline( plik, wiersz ) )
std::cout << wiersz << std::endl;
plik.close();
return true;
}
int main()
{
if( !wyswietlZawartoscPliku( "cpp0x.txt" ) )
std::cout << "Nie udalo sie otworzyc pliku o podanej nazwie." << std::endl;
return 0;
}
Jeżeli skorzystałeś z pliku tekstowego, wspomnianego w niniejszym kursie, to wówczas w oknie konsoli powinieneś zobaczyć następującą treść:
--==[ Kurs C++ | http://cpp0x.pl ]==--
Obecnie uczysz sie obslugi plikow. Link do czytanego rozdzialu:
http://cpp0x.pl/kursy/Kurs-C++/Poziom-4/Wczytywanie-tekstu-z-pliku/355
Udalo Ci sie odczytac plik?
Wykonaj teraz prace domowa ze wspomnianego rozdzialu! :)
Podsumowanie
Po ukończeniu tego rozdziału powinieneś wiedzieć jak otworzyć plik tekstowy, jak odczytać jego zawartość oraz jak prawidłowo należy kończyć z nim pracę. Jeżeli uważasz, że zrozumiałeś treść całego niniejszego rozdziału, zachęcam Cię do próby samodzielnego rozwiązania pracy domowej.
Zadanie domowe
Napisz program, który odczyta zawartość pliku, a następnie wypisze na ekranie tylko te wiersze, w których znajduje się wyraz wprowadzony przez użytkownika.