Obsługa plików w C++ – odczyt danych
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ło by 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.h
Kiedy 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; do { getline(uchwyt, linia); //pobierz linijkę cout << linia << endl; //wypisz na ekranie } while(linia != ""); //przerwij jeżeli linia będzie pusta (dane w pliku się skończą) UWAGA: Pamiętaj, żeby w pliku zostawić ostatnią linijkę pustą 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("scierzkaDoPliku.txt", "parmetr");
Za scierzkaDoPliku.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+ | Tworzony 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; //wypsiuję 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_oczytu, plik_na_ktorym_wykonujemy_operacje);
Jak widać nie ma w tym nic trudnego.
Fot: Alexandre Buisse (Nattfodd).
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 <>)
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.
do
{
getline(uchwyt, linia); //pobierz linijkę
cout << linia << endl; //wypisz na ekranie
}
while(linia == ""); //przerwij jeżeli linia będzie pusta (dane w pliku się skończą)
Pyt.: Czy ten warunek while (linia == "") nie oznacza "powtarzaj pętelę jeżeli linia jest pusta"? Nie rozumiem dlaczego taki warunek miałby oznaczać, że w linii coś się znajduje. Ktoś wyjaśni?
Faktycznie tam powinno być while(linia != „”), wtedy mamy false w momencie, w którym trafimy na pustą linijkę – ważne jest, żeby taka była na końcu bo inaczej program będzie się wykonywał w nieskończoność.
Nie w nieskończoność tylko do pierwszego zaniku prądu w wypadku komputerów stacjonarnych lub pierwszej usterki technicznej.
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. Plikdocx
(Worda) nie składa się TYLKO z tekstu, jaki wpisałeś w edytorze. Zawiera on szereg innych danych. Aby to zobaczyć, zmień rozszerzeniedocx
natxt
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