Pisząc programy komputerowe na pewno, każdy kiedyś zetknął się z obsługą plików. To właśnie między innymi dzięki plikom, możemy zapisywać ważne informacje wykorzystywane w naszym programie co podniesie jego użyteczność. Po co zresztą komuś aplikacja, która nie potrafi zapamiętać wcześniej wprowadzonych informacji?
Obsługa plików w C++ nie jest tak bardzo skomplikowana jak mogłoby się wydawać początkującym programistom, w niniejszym artykule zaprezentuję na praktycznych przykładach sposoby odczytu danych z plików tekstowych oraz binarnych.
Pliki możemy odczytywać za pomocą bibliotek fstream, stdio.h, które zanim zaczniemy cokolwiek robić musimy najpierw zaimportować do naszego programu:
#include <fstream> //import biblioteki fstream
#include <stdio.h> //import biblioteki stdio.hKiedy już dołączyliśmy którąś z bibliotek na przykład fstream to możemy się zabrać do dalszej pracy.
getline()
Napiszmy sobie program, który pobiera dane z pliku tekstowego linijka po linijce za pomocą funkcji getline(); i wypisuje je na ekranie. Funkcja getline(uchwytDoPliku, dane); pobiera jedną linijkę z pliku tekstowego.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream uchwyt; //obiekt typu fstream (uchwyt do pliku)
uchwyt.open("plik.txt"); //otwieramy plik: plik.txt (plik - nazwa pliku, txt - rozszerzenie)
string linia;
while(getline(uchwyt, linia)) //pobieraj kolejne linijki aż do końca pliku
{
cout << linia << endl; //wypisz na ekranie
}
uchwyt.close(); //zamykamy plik
return 0;
}Powyższy program wyświetli nam na ekranie zawartość danego pliku tekstowego pobierając wszystkie dane linijka po linijce.
Otwieranie pliku za pomocą funkcji fopen()
Korzystając z biblioteki stdio.h będziemy korzystali z funkcji fopen() do otwarcia pliku. Zobaczmy jak się nią posługiwać:
Ogólna składnia funkcji fopen():
fopen("sciezkaDoPliku.txt", "parametr");Za sciezkaDoPliku.txt wstawiamy ścieżkę do pliku który chcemy otworzyć względem naszego pliku z kodem, a w miejscu parametru określamy tryb dostępu do pliku:
| Tryb dostępu do pliku | Opis |
|---|---|
a |
Otwiera plik tylko do zapisu (jeśli plik nie został stworzony, to zostanie utworzony, wszystkie nowe dane będą zapisywane na jego końcu) |
a+ |
Otwiera plik do odczytu i zapisu (jeśli plik nie został stworzony, to zostanie utworzony, wszystkie nowe dane będą zapisywane na jego końcu, można odczytywać dowolny fragment pliku) |
b |
Otwiera plik w trybie binarnym |
r |
Otwiera plik tylko do odczytu (wcześniej musi on zostać stworzony) |
r+ |
Otwiera plik do odczytu i zapisu (wcześniej musi on zostać stworzony, można odczytywać i zapisywać dowolny fragment pliku) |
w |
Tworzy nowy plik i otwiera tylko do zapisu (uchwyt ustawiany jest na samym początku, a w przypadku kiedy dany plik już istniał to kasowana jest jego zawartość) |
w+ |
Tworzy nowy plik i otwiera go do odczytu i zapisu (uchwyt ustawiany jest na samym początku, a w przypadku kiedy dany plik już istniał to kasowana jest jego zawartość, można odczytywać i zapisywać dowolny fragment pliku) |
Przykład:
FILE* plik; //uchwyt do pliku
plik = fopen("plik.txt", "r"); //otwieramy plik w trybie tylko do odczytu - parametr "r"fgetc()
Oczywiście możemy na przykład chcieć pobierać tekst z danego pliku znak po znaku. Problem ten można rozwiązać na przykład wykorzystując bibliotekę stdio.h i funkcję fgetc();. Osobiście wolę właśnie używać tej biblioteki do obsługi plików w C++ gdyż metoda ta jest znacznie szybsza oraz mam większą kontrolę nad pobieranymi danymi i wykonywanymi operacjami.
Napiszmy więc program, który będzie pobierał dane z pliku tekstowego znak po znaku.
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
FILE* plik; //uchwyt do pliku
plik = fopen("plik.txt", "r"); //otwieramy plik w trybie tylko do odczytu - parametr "r"
char znak;
do
{
znak = fgetc(plik); //zapisuję jeden znak z pliku
cout << znak << endl; //wypisuję na ekran
}
while(znak != EOF); //End Of File - koniec pliku
fclose(plik); //zamykamy plik
return 0;
}Funkcja fopen(„plik”, „trybOtwarcia”); może otwierać pliki w różnych trybach, na przykład do odczytu lub zapisu. Sposób działania tej funkcji omówiłem w poprzednim akapicie „Otwieranie pliku za pomocą funkcji fopen()„.
fgets()
Funkcja fgets() odczytuje dane z wybranego wejścia (na przykład pliku) do momentu napotkania znaku nowej linii bądź do momentu wczytania określonej liczby znaków.
Przykład:
#include <stdio.h>
using namespace std;
int main()
{
FILE *plik;
char wczytaneZnaki[10];
plik = fopen("plik.txt", "r"); //otwarcie pliku
fgets(wczytaneZnaki, 10, plik);
for(int i = 0;i<10;i++)
cout << wczytaneZnaki[i];
fclose(plik); //zamknięcie pliku
return 0;
}Ogólna składnia funkcji fgets():
fgets(obiektDoKtoregoZapisujemyDane, maksymalnaLiczbaZnakow, wejscie);fscanf()
fscanf() działa bardzo podobnie do scanf(). Funkcję fscanf() można jednak wykorzystać do odczytania danych z pliku. Przyjrzyjmy się więc jak to zrobić.
Przykład:
#include <stdio.h>
using namespace std;
int main ()
{
char wczytywaneZnaki[10];
FILE *plik;
plik = fopen("plik.txt", "a+");
fscanf(plik, "%s", wczytywaneZnaki);
fclose(plik); //zamknięcie pliku
for(int i = 0;i<10;i++)
cout << wczytywaneZnaki[i];
return 0;
}Ogólna składnia:
fscanf(wejscie, specyfikator, obiektWyjscia);W miejscu specyfikator podajemy dane z poniżej tabelki:
| Specyfikator | Opis |
|---|---|
%d lub %i |
Liczba całkowita ze znakiem (system dziesiętny) np. int |
%f |
Liczba rzeczywista (system dziesiętny) np. float |
%u |
Liczba całkowita bez znaku (system dziesiętny) |
%e |
Liczba w notacji naukowej – ze znakiem e (małe litery) |
%E |
Liczba w notacji naukowej – ze znakiem E (duże litery) |
%g |
Liczba rzeczywista w notacji %f lub %e |
%G |
Liczba rzeczywista w notacji %f lub %E |
%o |
Liczba w systemie ósemkowym |
%x |
Liczba w systemie szesnastkowym (małe litery) |
%X |
Liczba w systemie szesnastkowym (duże litery) |
%c |
Znak np. zmienne typu char |
%s |
Łańcuch znaków zakończony “\0“, np. zmienne typu string |
%p |
Adres pamięci |
fread()
Napiszmy teraz program, który odczyta nam dane z pliku binarnego.
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
{
FILE* plik;
plik = fopen("plik.dat", "rb"); //otwarcie pliku jako plik binarny - parametr "b"
int liczba;
fread(&liczba, sizeof(int), 1, plik); //fread(wskaźnik na zmienną do której zapisujemy dane, rozmiar elementu, ilość liczb do odczytu, plik na którym wykonujemy operację);
cout << liczba << endl; //wypisz dane
fclose(plik); //zamykamy plik
return 0;
}W powyższym programie do odczytu danych binarnych została wykorzystana funkcja fread();, której składnia wygląda następująco:
fread(wskaznik_na_zmienna_do_ktorej_zapisujemy_dane, rozmiar_elementu, ilosc_liczb_do_odczytu, plik_na_ktorym_wykonujemy_operacje);Jak widać nie ma w tym nic trudnego.
Fot: Alexandre Buisse (Nattfodd).
Zapis danych do pliku
Odczyt mamy już opanowany, więc naturalnym krokiem jest zapis. Zaczynamy tak samo, od fopen(), ale tym razem kluczowy jest tryb otwarcia pliku. To on decyduje, czy plik nadpisujemy, czy dopisujemy do niego dane:
"r"– tylko odczyt (plik musi istnieć),"w"– zapis, tworzy nowy plik, a istniejący nadpisuje (kasuje dotychczasową zawartość),"a"– dopisywanie na końcu pliku (ang. append), bez kasowania,"r+","w+","a+"– warianty z jednoczesnym odczytem i zapisem.
Do samego zapisu tekstu najczęściej używamy fprintf() (działa jak printf, tyle że pisze do pliku) oraz fputs(). Poniższy przykład tworzy plik i zapisuje w nim dwie linie:
#include <stdio.h>
int main(void) {
FILE *plik = fopen("dane.txt", "w"); // tryb "w" - zapis
if (plik == NULL) {
printf("Nie udalo sie otworzyc pliku!\n");
return 1;
}
fprintf(plik, "Imie: %s, wiek: %d\n", "Anna", 30);
fputs("Druga linia tekstu\n", plik);
fclose(plik); // zawsze zamykamy plik!
return 0;
}Zwróć uwagę na dwie rzeczy, o których łatwo zapomnieć. Zawsze sprawdzamy, czy fopen() nie zwrócił NULL (np. brak uprawnień do zapisu), i zawsze wywołujemy fclose(), bo bez tego część danych może zostać w buforze i nie trafić na dysk. Do zapisu danych binarnych (nie tekstu) służy z kolei fwrite(), będące odpowiednikiem fread().
Obsługa plików w C++ na strumieniach fstream
W C++ pracę z plikami wygodniej jest oprzeć na strumieniach z nagłówka <fstream>. Zamiast funkcji fopen/fclose używamy obiektów: ofstream do zapisu (ang. output file stream), ifstream do odczytu (ang. input file stream) oraz fstream do obu naraz. Operujemy na nich tak samo jak na cout i cin, za pomocą operatorów << i >>:
#include <fstream>
#include <iostream>
#include <string>
int main() {
// Zapis do pliku
std::ofstream zapis("dane.txt");
if (zapis.is_open()) {
zapis << "Imie: Anna, wiek: 30\n";
zapis << "Druga linia tekstu\n";
} // plik zamyka sie sam, gdy obiekt wychodzi z zakresu
// Odczyt linia po linii
std::ifstream odczyt("dane.txt");
std::string linia;
while (std::getline(odczyt, linia)) {
std::cout << linia << "\n";
}
return 0;
}Największa wygoda strumieni w C++ to automatyczne zamykanie pliku. Gdy obiekt ofstream czy ifstream wychodzi z zakresu (kończy się blok { }), jego destruktor sam wywołuje close(). Nie da się więc zapomnieć o zamknięciu pliku tak, jak zdarza się to z fclose() w C. Mechanizm ten (znany jako RAII) sprawia, że w nowym kodzie C++ zwykle sięgamy właśnie po fstream, a klasyczne fopen/fread zostawiamy dla języka C i sytuacji, w których musimy współpracować z jego API.
Tryby otwarcia pliku w fstream (ios::)
Strumienie z rodziny fstream przyjmują w metodzie open (lub konstruktorze) drugi argument: tryb otwarcia pliku. Tryby można łączyć znakiem |, na przykład ios::out | ios::binary. Do wyboru mamy:
| Tryb | Działanie |
|---|---|
| ios::app | Otwiera plik tylko do zapisu, ustawia wskaźnik na jego końcu i tam zapisuje dane. |
| ios::ate | Ustawianie wskaźnika na koniec pliku. |
| ios::binary | Otwarcie pliku w trybie binarnym, tzn. dane traktowane są jak binarne, a nie tekstowe. |
| ios::in | Otwarcie pliku w trybie do odczytu. |
| ios::out | Otwarcie pliku w trybie do zapisu. |
| ios::trunc | Podczas otwierania plik jest czyszczony (tzn. usuwana jest jego zawartość). |
W praktyce najczęściej przydaje się ios::app: otwarcie do zapisu z dopisywaniem na końcu pliku, bez kasowania dotychczasowej zawartości (odpowiednik trybu „a” z fopen opisanego wyżej). Domyślne ofstream działa jak ios::out i nadpisuje plik.



Dzięki, w najbliższym czasie postaram się zaktualizować artykuł o podane przez Ciebie elementy. Natomiast jeśli chodzi o fstream to już poprawiłem ;)
Miałeś dodać 'b’, czyli powinno być np „rb” :)
Dodałem „b” do tabelki jako osobny tryb otwierania pliku (tryb binarny)
nie chodzi o tabelkę, a o uzycie:
plik = fopen(„plik.dat”, „b”); //otwarcie pliku jako plik binarny – parametr „b”
winno być:
plik = fopen(„plik.dat”, „rb”); //otwarcie pliku jako plik binarny – parametr „b”
Faktycznie, dzięki ;)
Obsługa plików w C++ nie jest tak bardzo skomplikowana jak mogło by się wydawać początkującym programistOM!!
Ta forma też jest dopuszczalna: http://sjp.pl/programist%C4%85
ale nie w tym kontekscie.
– Kim jestem? – Jestem programistą.
– Komu się przyglądam? – Przyglądam się pracującym programistom.
Ok, prawiłem ;) Tego typu błędów niestety nie wyłapuje edytor.
Czy wie ktoś jak odczytać jaki jest AUTOR/WŁAŚCICIEL pliku w C?
Spróbuj tak:
#include
#include
#include
#include
#include
#include
#include
using namespace std;
void printStat(const char *, struct stat *);
int main(int argc, char ** argv)
{
if (argc > 1)
{
struct stat buf;
for (int i = 1; i < argc; ++i)
{
lstat(argv[i], &buf);
printStat(argv[i], &buf);
}
}
return 0;
}
void printStat(const char * fname, struct stat *data)
{
cout << dec << "Wlasciciel: " <st_uid << "n";
}
Wiem
Może pytanie dość naiwne, ale muszę ze względu że czas mnie goni z projektem.
Do rzeczy, jaka jest różnica w odczytywaniu plików .docx a .txt. Próbuję wykonać projekt który będzie obliczał funkcje skrótu dla tekstu z pliku. Wczytuję plik .txt z tekstem „hello world” i po wywołaniu funkcji dostaję pewny ciąg (skrót tego tekstu) natomiast wynik dla tego samego tekstu w pliku .docx jest inny. Wygląda to na złe odczytanie treści pliku, więc proszę o jakieś sugestię jak obsłużyć takie rozszerzenie
Cześć Robert,
Różnica jest taka, że plik txt odczytywany podanymi w artykule metodami, zwraca dokładnie taki tekst, jaki w nim zapiszesz. Plik docx (Worda) nie składa się TYLKO z tekstu, jaki wpisałeś w edytorze. Zawiera on szereg innych danych. Aby to zobaczyć, zmień rozszerzenie docx na txt i otwórz plik dowolnym edytorem. Z tego względu dalsze operacje nie działają, tak jak oczekujesz.
Pozdrawiam,
Łukasz Dudziński, autor bloga Strefakodera.pl
W fopen określamy nie tylko tryb dostępu do pliku, ale również rodzaj pliku – domyślnie otwierany jest tryb tekstowy, należałoby dodac 'b’, aby był tryb binarny
Do wczytywania danych w c masz jeszcze fgets i fscanf, nie tylko fgetc
natomiast w fstream nie tylko jest getline.
Dodatkowo fstream nie do końca jest uchwytem, jest całym obiektem z mnogością metod (nie omijając operatorów <>)