Przejdź do głównej zawartości

Obsługa plików

Wymaga znajomości: 1. Pierwszy program - 9. Metody oraz 11. Referencje

W tym artykule pokażemy Ci jak poprawnie korzystać z plików w języku C++.

Motywacja

Używanie plików 📝 jest często niezbędną częścią aplikacji. Programy przechowują swoje dane między innymi na dysku komputerowym. Aplikacje biurowe zapisują dokumenty utworzone przez użytkownika. Często korzystają z plików konfiguracyjnych, które również znajdują się na dysku. Gry wideo trzymają modele, dźwięki, tekstury i inną tego typu zawartość wewnątrz swoich plików.

Wyświetlanie zawartości pliku
Wyświetlanie zawartości pliku

Operacje na pliku

Wstęp

W kodzie z tego artykułu będziemy korzystali z następującego nagłówka z biblioteki standardowej:

#include <fstream>

Pozwoli on nam na obsługę pojedynczego pliku i jego zawartości, w tym:

  • stworzenie pliku
  • odczyt danych
  • zapisanie danych

Otwieranie pliku

Żeby w ogóle móc korzystać z zawartości pliku, musimy go otworzyć za pomocą odpowiedniego obiektu z biblioteki standardowej. W zależności od tego czy chcemy jedynie odczytywać dane, czy tylko zapisywać, czy robić obydwie te rzeczy, użyjemy obiektu innego typu.

Przy obsłudze plików w języku C++ korzystamy z obiektów typu std::fstream oraz jego wariantów, w postaci std::ifstream i std::ofstream.

auto file = std::ifstream("plik.txt");
auto file = std::ofstream("plik.txt");
Przedrostki

Zwróć uwagę na różnicę std::ifstream (ang.: input file stream) i std::ofstream (ang.: output file stream).

Aby otworzyć plik w obydwu trybach jednocześnie używamy std::fstream (bez przedrostka i ani o).

auto file = std::fstream("plik.txt");

Odczyt i zapis danych

Z obiektu file, który utworzyliśmy wyżej korzystamy w bardzo podobny sposób do tego jak używaliśmy std::cin oraz std::cout. Interfejsy tych obiektów zostały w bibliotece standardowej ujednolicone - każdy z nich jest "strumieniem", na którym możemy wykonywać operacje za pomocą operatorów << oraz >>:

Utwórz ręcznie plik plik.txt w ogólnodostępnym miejscu na dysku, np. utwórz folder zadanie na dysku C (na Windowsie) - C:/zadanie/plik.txt lub folderze użytkownika (na Linuxie: /home/username/).

Zawartość 'plik.txt'
ala ma 1 kota
oraz 2 psy

Otwarcie pliku

Ścieżki bezwzględne

Na tym etapie będziemy posługiwać się tylko ścieżkami bezwzględnymi, czyli takimi które precyzyjnie określają położenie pliku na komputerze. Czytając artykuł dalej, poznasz też ścieżki względne, które zawsze określają położenie względem tzw. folderu roboczego.

Używamy wcześniej poznanej metody:

#include <iostream>
#include <fstream>

int main() {
auto file = std::fstream("C:/zadanie/plik.txt");

// dalsze operacje
}
Slash i backslash

System Windows używa domyślnie znaku backslash (\) jako separatora w ścieżkach. Ten sam znak ma również specjalne znaczenie w tekście w C++. Podając ścieżkę do pliku ręcznie w kodzie, np:

auto file = std::ifstream("C:\\zadanie\\plik.txt");

użyj podwójnego znaku backslash (\\) tak jak powyżej, lub zastąp je znakiem slash (/).

Odczyt

Plik zawiera dwie linie, które są podzielone na "słowa" - ala, ma, 1, kota itd.

Odczyt za pomocą >>

Tak jak w przypadku std::cin wczytywanie danych za pomocą operatora >> zatrzymuje się na pierwszym białym znaku - spacji, tabulacji czy nowej linii.

Wyświetlmy je w pętli:

// ⚠ nie zapomnij o include <string>
std::string word;
while (file >> word) {
std::cout << "Slowo: " << word << '\n';
}

Zauważ, że odczyt file >> word umieściliśmy w warunku pętli, ponieważ po próbie wykonania odczytu, operator>> zwróci wynik, który pozwoli nam określić czy operacja się powiodła czy nie.

Ciekawostka

Wynik operatora >> jest konwertowalny na typ bool (true / false), więc może zostać wstawiony do warunku pętli.

W naszym wypadku niepowodzenie może następić dopiero przy końcu pliku.

Wczytywanie całych linii

Aby wczytać całą linię z pliku, używamy funkcji std::getline, w ten sposób:

Wczytanie linii z pliku
std::string line;
std::getline(file, line);

Aby wczytać cały plik linia po linii, umieszczamy getline w warunku pętli while, tak samo jak zrobiliśmy wyżej z operatorem >>:

Wczytanie całego pliku
std::string line;
while (std::getline(file, line)) {
std::cout << "Linia: " << line << '\n';
}

Zapis

Chcąc zapisać dane do pliku, skorzystamy z tego samego operatora co przy std::cout:

int age = 23;
file << "Mam " << age << " lata\n";

Operator << potrafi korzystać z tych samych typów zmiennych co std::cout.

Pozycja operacji w pliku

Operacje zapisu i odczytu na pliku są wykonywane względem wewnętrznej pozycji w pliku.

Tryby otwarcia

Tworząc obiekt typu std::fstream lub jego wariantów, możemy opcjonalnie określić w drugim parametrze tryb otwarcia pliku:

auto file = std::fstream("plik.txt", tryb);

Dopuszczalne wartości:

TrybOpis
std::ios::indo odczytu
std::ios::outdo zapisu
std::ios::appdo zapisu, ale zawsze dopisujemy na koniec pliku
std::ios::truncdo zapisu, ale przy otwarciu zawartość jest tracona
std::ios::binarytryb binarny (więcej o tym niżej)
std::ios::ateustawia domyślną pozycję zapisu i odczytu na koniec

Podane poniżej tryby można ze sobą łączyć za pomocą operatora |.

Domyślne tryby

std::fstream domyślnie otwiera plik w trybie std::ios::in | std::ios::out (do odczytu i do zapisu):

auto file = std::fstream("plik.txt");

Ten zapis jest równoważny z tym:

auto file = std::fstream("plik.txt", std::ios::in | std::ios::out);

Każda z trzech klas: std::fstream, std::ifstream i std::ofstream posiada swoje domyślne tryby otwarcia. Oto one:

KlasaDomyślny tryb
std::fstreamstd::ios::in | std::ios::out
std::ifstreamstd::ios::in
std::ofstreamstd::ios::out

Tryb binarny

Przykłady

Uwaga!

Ta sekcja wymaga rozbudowy. Możesz nam pomóc edytując tą stronę.

Potencjalne błędy

Niepoprawne wykorzystanie eof()

Często możemy natknąć się na kod, który odczytuje dane w ten sposób:

❌ Źle
std::string word;
while (!file.eof())
{
file >> word;
std::cout << word << '\n';
}

Metoda eof() sprawdza czy dotarliśmy do końca pliku (ang.: end of file), jednak to podejście jest niepoprawne! Zauważ, że nie sprawdzamy czy operacja >> się powiodła przed wypisaniem word!

Przekazywanie do funkcji bez referencji

Obiekty typu std::fstream i jego wariacje zawsze przekazujemy do funkcji przez referencję, ponieważ kopiowanie ich do parametru jest zabronione!

auto readLines(std::fstream file) // ❌ Uwaga: brak referencji
{
auto lines = std::vector<std::string>();

std::string line;
while (std::getline(file, line))
lines.push_back(line);

return lines;
}

Obsługa plików

Wymaga znajomości: 1. Pierwszy program - 9. Metody oraz 11. Referencje

W tym artykule pokażemy Ci jak poprawnie korzystać z plików w języku C++.

Motywacja

Używanie plików 📝 jest często niezbędną częścią aplikacji. Programy przechowują swoje dane między innymi na dysku komputerowym. Aplikacje biurowe zapisują dokumenty utworzone przez użytkownika. Często korzystają z plików konfiguracyjnych, które również znajdują się na dysku. Gry wideo trzymają modele, dźwięki, tekstury i inną tego typu zawartość wewnątrz swoich plików.

Wyświetlanie zawartości pliku
Wyświetlanie zawartości pliku

Operacje na pliku

Wstęp

W kodzie z tego artykułu będziemy korzystali z następującego nagłówka z biblioteki standardowej:

#include <fstream>

Pozwoli on nam na obsługę pojedynczego pliku i jego zawartości, w tym:

  • stworzenie pliku
  • odczyt danych
  • zapisanie danych

Otwieranie pliku

Żeby w ogóle móc korzystać z zawartości pliku, musimy go otworzyć za pomocą odpowiedniego obiektu z biblioteki standardowej. W zależności od tego czy chcemy jedynie odczytywać dane, czy tylko zapisywać, czy robić obydwie te rzeczy, użyjemy obiektu innego typu.

Przy obsłudze plików w języku C++ korzystamy z obiektów typu std::fstream oraz jego wariantów, w postaci std::ifstream i std::ofstream.

auto file = std::ifstream("plik.txt");
auto file = std::ofstream("plik.txt");
Przedrostki

Zwróć uwagę na różnicę std::ifstream (ang.: input file stream) i std::ofstream (ang.: output file stream).

Aby otworzyć plik w obydwu trybach jednocześnie używamy std::fstream (bez przedrostka i ani o).

auto file = std::fstream("plik.txt");

Odczyt i zapis danych

Z obiektu file, który utworzyliśmy wyżej korzystamy w bardzo podobny sposób do tego jak używaliśmy std::cin oraz std::cout. Interfejsy tych obiektów zostały w bibliotece standardowej ujednolicone - każdy z nich jest "strumieniem", na którym możemy wykonywać operacje za pomocą operatorów << oraz >>:

Utwórz ręcznie plik plik.txt w ogólnodostępnym miejscu na dysku, np. utwórz folder zadanie na dysku C (na Windowsie) - C:/zadanie/plik.txt lub folderze użytkownika (na Linuxie: /home/username/).

Zawartość 'plik.txt'
ala ma 1 kota
oraz 2 psy

Otwarcie pliku

Ścieżki bezwzględne

Na tym etapie będziemy posługiwać się tylko ścieżkami bezwzględnymi, czyli takimi które precyzyjnie określają położenie pliku na komputerze. Czytając artykuł dalej, poznasz też ścieżki względne, które zawsze określają położenie względem tzw. folderu roboczego.

Używamy wcześniej poznanej metody:

#include <iostream>
#include <fstream>

int main() {
auto file = std::fstream("C:/zadanie/plik.txt");

// dalsze operacje
}
Slash i backslash

System Windows używa domyślnie znaku backslash (\) jako separatora w ścieżkach. Ten sam znak ma również specjalne znaczenie w tekście w C++. Podając ścieżkę do pliku ręcznie w kodzie, np:

auto file = std::ifstream("C:\\zadanie\\plik.txt");

użyj podwójnego znaku backslash (\\) tak jak powyżej, lub zastąp je znakiem slash (/).

Odczyt

Plik zawiera dwie linie, które są podzielone na "słowa" - ala, ma, 1, kota itd.

Odczyt za pomocą >>

Tak jak w przypadku std::cin wczytywanie danych za pomocą operatora >> zatrzymuje się na pierwszym białym znaku - spacji, tabulacji czy nowej linii.

Wyświetlmy je w pętli:

// ⚠ nie zapomnij o include <string>
std::string word;
while (file >> word) {
std::cout << "Slowo: " << word << '\n';
}

Zauważ, że odczyt file >> word umieściliśmy w warunku pętli, ponieważ po próbie wykonania odczytu, operator>> zwróci wynik, który pozwoli nam określić czy operacja się powiodła czy nie.

Ciekawostka

Wynik operatora >> jest konwertowalny na typ bool (true / false), więc może zostać wstawiony do warunku pętli.

W naszym wypadku niepowodzenie może następić dopiero przy końcu pliku.

Wczytywanie całych linii

Aby wczytać całą linię z pliku, używamy funkcji std::getline, w ten sposób:

Wczytanie linii z pliku
std::string line;
std::getline(file, line);

Aby wczytać cały plik linia po linii, umieszczamy getline w warunku pętli while, tak samo jak zrobiliśmy wyżej z operatorem >>:

Wczytanie całego pliku
std::string line;
while (std::getline(file, line)) {
std::cout << "Linia: " << line << '\n';
}

Zapis

Chcąc zapisać dane do pliku, skorzystamy z tego samego operatora co przy std::cout:

int age = 23;
file << "Mam " << age << " lata\n";

Operator << potrafi korzystać z tych samych typów zmiennych co std::cout.

Pozycja operacji w pliku

Operacje zapisu i odczytu na pliku są wykonywane względem wewnętrznej pozycji w pliku.

Tryby otwarcia

Tworząc obiekt typu std::fstream lub jego wariantów, możemy opcjonalnie określić w drugim parametrze tryb otwarcia pliku:

auto file = std::fstream("plik.txt", tryb);

Dopuszczalne wartości:

TrybOpis
std::ios::indo odczytu
std::ios::outdo zapisu
std::ios::appdo zapisu, ale zawsze dopisujemy na koniec pliku
std::ios::truncdo zapisu, ale przy otwarciu zawartość jest tracona
std::ios::binarytryb binarny (więcej o tym niżej)
std::ios::ateustawia domyślną pozycję zapisu i odczytu na koniec

Podane poniżej tryby można ze sobą łączyć za pomocą operatora |.

Domyślne tryby

std::fstream domyślnie otwiera plik w trybie std::ios::in | std::ios::out (do odczytu i do zapisu):

auto file = std::fstream("plik.txt");

Ten zapis jest równoważny z tym:

auto file = std::fstream("plik.txt", std::ios::in | std::ios::out);

Każda z trzech klas: std::fstream, std::ifstream i std::ofstream posiada swoje domyślne tryby otwarcia. Oto one:

KlasaDomyślny tryb
std::fstreamstd::ios::in | std::ios::out
std::ifstreamstd::ios::in
std::ofstreamstd::ios::out

Tryb binarny

Przykłady

Uwaga!

Ta sekcja wymaga rozbudowy. Możesz nam pomóc edytując tą stronę.

Potencjalne błędy

Niepoprawne wykorzystanie eof()

Często możemy natknąć się na kod, który odczytuje dane w ten sposób:

❌ Źle
std::string word;
while (!file.eof())
{
file >> word;
std::cout << word << '\n';
}

Metoda eof() sprawdza czy dotarliśmy do końca pliku (ang.: end of file), jednak to podejście jest niepoprawne! Zauważ, że nie sprawdzamy czy operacja >> się powiodła przed wypisaniem word!

Przekazywanie do funkcji bez referencji

Obiekty typu std::fstream i jego wariacje zawsze przekazujemy do funkcji przez referencję, ponieważ kopiowanie ich do parametru jest zabronione!

auto readLines(std::fstream file) // ❌ Uwaga: brak referencji
{
auto lines = std::vector<std::string>();

std::string line;
while (std::getline(file, line))
lines.push_back(line);

return lines;
}