Podręcznik
3. Pliki tekstowe
Wczytywanie danych z pliku odbywa się w sposób analogiczny jak w przypadku strumieniowej obsługi strumienia wejścia oraz wyjścia konsoli. W przypadku obsługi konsoli wykorzystywaliśmy bibliotekę iostream, natomiast w przypadku obsługi plików tekstowych wykorzystywana jest biblioteka fstream.
W tej bibliotece znajdziemy dwie główne klasy do obsługi plików:
std::ifstream
– służy do strumieniowego odczytu z plików.std::ofstream
– służy do strumieniowego zapisu do plików.
Do otwarcia pliku używamy obiektu jednej z klas strumieniowych i metody open()
, podając jako argument nazwę pliku oraz (opcjonalnie) tryb otwarcia.
ifstream plk_we; // zmienna plk_we określa plik do odczytu
plk_we.open("dane.txt");
Domyślnie katalog roboczy to:
- Katalog, w którym znajduje się plik wykonywalny, jeśli uruchamiasz program bezpośrednio.
- Katalog, z którego uruchamiasz program z terminala lub konsoli.
#include <iostream>
#include <filesystem> // C++17
int main() {
std::cout << std::filesystem::current_path() << std::endl;
return 0;
}
std::filesystem::current_path()
(dostępna od C++17) zwróci aktualny katalog roboczy, co pozwoli zobaczyć, skąd program próbuje otworzyć plik.Przy wpisywaniu ścieżek dostępu należy być bardzo ostrożnym. W stałych napisowych w C++ znak odwróconego ukośnika (backslash) jest znakiem specjalnym, więc jeśli chcecie uzyskać go jako znak, musi wystąpić dwa razy. Tak więc ścieżkę C:\katalog\plik w kodzie C++ zapisujemy "C:\\katalog\\plik". Możemy także zastosować zapis "C:/katalog/plik".
Aby funkcja działała w sposób prawidłowy nalęzy upewnić się że plik istnieje i jest możliwy jego odczyt. W tym celu możemy wykorzystać dwie metody good()
lub nowszej i bardziej uniwersalnej is_open()
.
int main() {
…
dane.open (... );
if ( !dane.is_open() ) { // jeśli nie ma podanego pliku
cout << " Blad otwarcia pliku" << endl ;
return 1; // błędne zakończenie pracy programu
}
// dalej działania na plikach
// i cala reszta programu
return 0; // poprawne zakończenie pracy programu
}
is_open()
służy do sprawdzenia, czy plik został poprawnie otwarty, zwracając true, jeśli plik jest otwarty, a false, jeśli otwarcie się nie powiodło. Nie monitoruje ona stanu operacji odczytu czy zapisu. Z kolei metoda good()
ocenia ogólny stan strumienia, sprawdzając, czy wszystkie operacje przebiegały bez błędów, w tym odczyt i zapis, oraz czy strumień nadal działa poprawnie. Metoda good()
wykrywa błędy takie jak koniec pliku lub problemy z odczytem, a is_open()
dotyczy wyłącznie poprawności otwarcia pliku.
Nazwa pliku otwieranego do odczytu lub zapisu może być wczytywana, czyli może byc zmienną, ale w tym szczególnym przypadku zmienna ta nie może być typu string
. W przypadku straszych wersji C++ (np. C++98 lub C++03), funkcje takie jak open()
w klasie std::ifstream
czy std::ofstream
wymagały podania ścieżki pliku w postaci wskaźnika do tablicy znaków typu const char*
. W tych starszych wersjach C++, obiekty typu std::string
nie były automatycznie konwertowane na tablice znaków, dlatego konwersja przez c_str()
była potrzebna. Przykład w starszych wersjach C++:std::string nazpl = "dane.txt";
std::ifstream dane;
dane.open(nazpl.c_str()); // Konwersja std::string na const char* jest konieczna
open()
przyjmowały również argumenty typu std::string
, dzięki czemu wywołanie c_str()
nie jest już konieczne. Można bezpośrednio przekazywać obiekt typu std::string
do metody open()
.std::string nazpl = "dane.txt";
std::ifstream dane;
dane.open(nazpl); // Nie trzeba używać c_str() w C++11 i nowszych
std::ios::in
– otwarcie pliku do odczytu,std::ios::out
– otwarcie pliku do zapisu (usuwa poprzednią zawartość pliku),std::ios::app
– otwarcie pliku w trybie dopisywania (dodawanie danych na końcu pliku),std::ios::binary
– otwarcie pliku w trybie binarnym,std::ios::ate
– otwarcie pliku i ustawienie wskaźnika na koniec.
dane.open("nazwa_pliku.bin", std::ios::binary | std::ios::app);
close()
.dane.close();
Dane z pliku odczytywane są w sposób strumieniowy, analogicznie jak w przypadku odczytu danych z klawiatury.
Ze strumienia można wczytywać bezproblemowo zmienne typów prostych, przy czym zawsze wykonana zostanie odpowiednia konwersja i rzutowanie. zmienna_plikowa >> zmienna_1 >> zmienna_2 >> ... >> zmienna_n;
Często istnieje konieczność sprawdzenia czy wczytany został znak końca pliku. W tym celu możemy wykorzystać metodę
eof()
, która sprawdza, czy osiągnięto koniec pliku (ang. end of file). Metodę możemy wykorzystać w połączeniu z pętlą while w celu wczytywania danych z pliku do momentu, gdy zostaną wczytane wszystkie dane.while (!plk.eof())
// dopóki nie napotkano końca pliku, wykonuj
// zmienna, do której wczytujemy linię
string linia;
...
// Nasza zmienna plikowa nazywa się plk
getline(plk, linia);
<fstream>
. Aby zapisać dane, należy utworzyć obiekt std::ofstream
i otworzyć plik, podając jego nazwę. Jeśli plik nie istnieje, zostanie utworzony, a jeśli istnieje, jego zawartość zostanie nadpisana (chyba że użyjemy trybu dopisywania std::ios::app
).std::ofstream plk;
plk.open("plik.txt");
<<
, podobnie jak przy wypisywaniu na konsolę.
plk << "Wartosc zmiennej: " << zmienna << endl;
close()
, aby upewnić się, że wszystkie dane zostały poprawnie zapisane i zasoby zostały zwolnione.