Interakcja z plikami
Interakcja z plikami
W języku C++ do interakcji z plikami służy klasa fstream. Jeżeli chcemy mieć możliwość edycji lub też odczytu danych zawartych w pliku, tworzymy zmienną, która będzie obsługiwała ten plik.
Chcąc uzyskać dostęp do klasy fstream, importujemy bibliotekę fstream. Następnie definiujemy zmienną plikową i używamy funkcji open(), której wymaganym argumentem jest ścieżka do pliku.
Podana ścieżka może być relatywnarelatywna (względna) lub absolutna (bezwzględna). Jeżeli plik tekstowy znajduje się w tym samym folderze, podajemy tylko nazwę pliku wraz z rozszerzeniem, np. plik.open("plik.txt");.
Jeśli natomiast plik tekstowy znajduje się w innym katalogu, podajemy ścieżkę absolutnąścieżkę absolutną. Ścieżka zależna jest od systemu operacyjnego, np. w systemie Windows podajemy literę oznaczającą dysk i wszystkie foldery, przez które należy przejść, aby przetworzyć plik, np.: plik.open("C:\\Pliki\\Dane\\plik.txt");.
W dystrybucjach Linuksa pliki zapisywane są w katalogach domowych użytkowników umieszczonych w katalogu /home/nazwa_uzytkownika. Ścieżkę rozpoczyna znak ukośnika (ang. slash), który oznacza katalog główny, np.: plik.open("/home/nazwa_uzytkownika/Dokumenty/plik.txt");.
Drugi argument, który możemy przekazać funkcji open(), to flagaflaga oznaczająca tryb otwarcia pliku. Oto przykładowe flagi:
ios::app– otwarcie pliku w trybie dopisywania, dane znajdujące się w pliku zostają zachowane, nowe dane dopisywane są na końcu pliku, odczytywanie danych nie jest możliwe; flaga wymaga połączenia z flagąios::out;ios::in– pozwala na odczytywanie danych z pliku;ios::out– pozwala na zapisywanie danych do pliku;ios::trunc– usunięcie danych z pliku podczas jego otwierania; flaga wymaga połączenia z flagąios::out(nie można natomiast łączyć jej z flagąios::app).
Zamiast nazwy klasy ios można używać nazwy klasy pochodnej fstream, np.: fstream::in, fstream::out.
Flagi możemy łączyć za pomocą symbolu |.
Zdefiniowana w ten sposób zmienna plik może być użyta do zapisu oraz do odczytu danych.
Kiedy mamy zmienną plikową, warto sprawdzić, czy jest opcja skorzystania z pliku. Można użyć do tego między innymi funkcji good(), która zwraca wartość true, jeżeli możemy używać pliku, lub wartość false, jeżeli nie ma opcji korzystania z pliku. Np. gdy nie mamy praw do jego otwarcia lub zapisu.
Jeżeli do utworzenia zmiennej plikowej używamy flag ios::in lub ios::in | ios::out, wartość true zwracana przez funkcję good() nie oznacza, że plik istnieje na dysku!
Po zakończeniu wszystkich działań na pliku należy użyć metody close(), która wymusza zapisanie wszystkich oczekujących w buforze danych do pliku, o ile istnieją, i zwalnia przydzielone zasoby. W podanych dalej przykładach pokazujemy jej stosowanie.
Zapisywanie danych do pliku
Szkielet programu, który pozwala na zapis do pliku, przedstawia się następująco:
Warto wspomnieć, że zamiast funkcji open() możemy stosować typową dla C++ konstrukcję wykorzystującą konstruktor klasy fstream: fstream zmienna("nazwa_pliku.txt", flagi). W przedstawionym kodzie napisalibyśmy:
Do zapisywania danych używamy jednego z dwóch omówionych wcześniej trybów otwarcia plików: ios::out lub ios::app. Tryb ios::out, jeżeli podany plik istnieje, usuwa jego zawartość. Flaga ios::app zachowuje poprzednie dane, a kolejne zapisuje na końcu.
Dane do pliku zapisujemy – podobnie jak podczas wypisywania ich na standardowy strumień wyjścia cout – używając operatora <<. Jeżeli chcemy usunąć wszystkie dane z pliku, a następnie zapisać w nim liczbę 21, wykonujemy to następująco:
Jeśli do pliku zamierzamy dopisać wartość 22, używamy flagi ios::app.
Warto pamiętać, że plik nie musi wcześniej istnieć. Jeśli go nie ma, zostanie utworzony.
Wczytywanie danych z pliku
Do wczytywania danych z pliku używamy tylko jednej flagi i jest nią ios::in. Omówimy dwa sposoby wczytywania danych tekstowych, które jednak nie nadają się do czytania danych binarnych.
Pierwszy sposób jest podobny do wczytywania danych przy wykorzystaniu standardowego strumienia wejścia cin i operatora >>. Jednak zamiast cin wpisujemy w tym przypadku nazwę zmiennej plikowej.
Otwarcie pliku do odczytu, podobnie jak do zapisu, możliwe jest również z pominięciem funkcji open():
Do zmiennej daneZPliku zapisywany będzie tekst z pliku – aż do napotkania znaku spacji, tabulatora lub też końca linii. Oznacza to, że jeżeli w pliku znajduje się wiersz zawierający tekst „21 22”, to do zmiennej daneZPliku zapisany zostanie ciąg „21”, ponieważ znaki „21” i „22” są rozdzielone spacją.
Jeżeli chcemy odczytać z pliku cały wiersz, używamy funkcji getline(). Funkcja ta jako pierwszy argument przyjmuje zmienną plikową, natomiast drugim argumentem jest zmienna, do której zostanie zapisany wiersz z pliku. Popatrzmy na przykład:
O wiele częściej będziemy chcieli odczytać wszystkie wiersze zawarte w pliku. Jednym ze sposobów jest użycie w pętli metody eof():
Podczas odczytu danych za pomocą pętli, metoda eof() w niektórych przypadkach może powodować błędy, ponieważ zwraca prawdę dopiero po tym, kiedy zostanie osiągnięty koniec pliku i nie wskazuje, że następna operacja odczytu będzie dotyczyła końca pliku.
Przykładem może być kod przedstawiony w filmie, którego zadaniem jest odczyt dwóch liczb z każdego wiersza pliku wejściowego i zapisanie ich w dwuwymiarowej tablicy liczb całkowitych:
Jeżeli w pliku dane.txt będzie 9 lub mniej wierszy zawierających dwie liczby oddzielone znakiem spacji oraz pusty ostatni wiersz, próba odczytu dwóch liczb w instrukcji plik >> liczby[i][0] >> liczby[i][1]; zostanie wykonana mimo końca pliku i zakończy się błędem: do tablicy zostaną zapisane dwie liczby zero, których w pliku nie ma. Funkcje eof() i fail() pozwalają wykryć tego typu sytuacje.
Zamiast konstrukcji while(!plik.eof()) lepiej więc stosować inne rozwiązania. W powyższym programie można użyć operatora bool(), który dodatkowo pozwala uprościć kod:
Metodę eof() można zastąpić funkcją getline(), która kończy wczytywanie, jeżeli napotyka koniec wiersza lub koniec pliku.
Słownik
stała podana jako argument, której wartość modyfikuje działanie funkcji
inaczej: bezwzględna; ścieżka do pliku, gdzie punktem startowym jest początek systemu plików
inaczej: względna; ścieżka do pliku, gdzie punktem startowym jest folder, w którym znajduje się program