Programowanie w Visual C++ 2010

Spis treści

Praca z plikami

Na wstępie...

Entuzjaści C++ znowu spieszą nam z pomocą! Opracowali specjalną bibliotekę <fstream>, która umożliwia pracę z plikami.

Jeżeli pragniesz obsługiwać w swoich programach pliki, umieść dyrektywę:

#include <fstream>
Pamiętasz, co mówiliśmy o obiektach i klasach w samouczku o napisach i liczbach zespolonych?

Zapis do pliku

Wprowadzenie

Gdy piszesz program w C++, czy nie brakuje Ci czasem możliwości zapamiętania rezultatów pracy? Przykładowo: przeprowadzasz skomplikowane obliczenia i potrzebujesz opracowania wyników w arkuszu kalkulacyjnym (np. w celu stworzenia przepięknych wykresów). Nie ma sensu próbowanie kopiowania wydruku z konsoli, bo to sprawa zazwyczaj problematyczna.

Tworzenie plików

Najpierw nauczymy się tworzyć pliki, do których będziemy zapisywać dane. Na pewno doskonale pamiętasz, iż by wyświetlić cokolwiek w konsoli, należy napisać cout << (console output). W przypadku zapisu do plików będzie podobnie, o czym zaraz się przekonamy.

Utwórz nowy projekt i wklej do niego poniższy kod:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
    ofstream zapis("plik.txt"); //ofstream - skrót od Output File STREAM
    zapis << "Hello World!"; //przypomina to trochę cout <<, nieprawdaż?
    zapis.close();
    return 0;
}

Pora przetłumaczyć powyższy listing na język zrozumiały dla człowieka. Gong Xi Fa Cai Xin Nian Kuai Le! :-)

A tak na poważnie... Linijka #7 mówi tyle: "Kochany Kompilatorze, zdefiniuj obiekt klasy ofstream o nazwie zapis. Przy okazji, w bieżącym katalogu roboczym utwórz plik plik.txt". Od tej pory będziemy zapisywać do tego pliku, operując właśnie na obiekcie zapis, co możemy zrobić, posługując się instrukcją przywołaną w linijce #8.

Jeżeli coś otworzyliśmy, to musimy również zamknąć (okna zamykamy po to, żeby nam nie napadało do pokoju; drzwi po to, aby ktoś obcy nie zabrał nam notatek z wykładu z AiPP, które zawsze leżą na biurku) – pracę z plikiem kończymy, wywołując metodę close() (patrz: wiersz #9).

Nie wierzysz, że na dysku powstał plik plik.txt? :-) Wystarczy, że teraz otworzysz folder, w którym znajdują się pliki źródłowe Twojego projektu – ujrzysz nowoutworzony plik.

Nie zapominajmy o zamykaniu plików! W przeciwnym razie efekty naszej pracy mogą nie zostać zachowane na dysku.
Utwórz program, który pobiera nazwę pliku, a następnie tworzy na dysku plik o podanej nazwie.

Otwieranie plików do ponownego zapisu

Zastanówmy się, co się stanie po wykonaniu instrukcji:

ofstream zapis;
zapis.open("plik.txt");

Możliwości są dwie:

  • jeżeli plik.txt istnieje na dysku, jego zawartość zostanie zastąpiona przez nowe dane,
  • w przeciwnym przypadku zostanie utworzony nowy plik o podanej nazwie.

Jeżeli chcesz otworzyć plik w celu dopisania nowej treści na jego końcu, skorzystaj z dodatkowego parametru konstruktora klasy ofstream:

ofstream zapis("plik.txt", ios_base::app); //otwieranie pliku w specjalnym trybie
//app - skrót od APPend, czyli "dołącz"

Błędy przy otwieraniu

Może się zdarzyć, że w wyniku braku miejsca na dysku, problemów z systemem operacyjnym lub innych błędów, nie będziemy w stanie otworzyć pliku. Na wszelki wypadek powinniśmy zabezpieczać się przed taką ewentualnością:

ofstream zapis("plik.txt");
if(!zapis) //Houston, we've got a problem!
{
    cout << "Blad otwarcia pliku!" << endl;
    exit(1); //przyjmuje się, że program zwraca wartość 1, gdy coś się nie powiodło
}

Zapis znak po znaku

Oprócz operatora << mamy do dyspozycji również inne sposoby zapisu do pliku. Na przykład:

	ofstream zapis("plik.txt");
	char znak;
	do
	{
		cin >> znak; //pobieramy znak
		zapis.put(znak); //zapisujemy go do pliku
	}
	while(znak!='?'); //czy znak '?' zapisze się w pliku?
	zapis.close();

Metoda put() umieszcza pojedynczy znak w pliku.

Zmień instrukcję z linijki #5 na:

znak=cin.get();

Czym się różnią wynikowe pliki?

Odczyt z pliku

Wprowadzenie

Załóżmy, że otrzymaliśmy mailem plik tekstowy od kolegi. Plik jednak okazał się być zaszyfrowany, aby nikt obcy nie mógł odczytać. Naszym zadaniem jest napisanie programu, który odczytuje zawartość pliku i ją odszyfrowuje (oczywiście, szyfr jest nam znany). Jak to zrobić? Zaraz dowiemy się, jak podejrzeć treść pliku.

Otwieranie pliku do odczytu

Dowiedzieliśmy się przed chwilą, że przy zapisywaniu danych do pliku, używaliśmy klasy ofstream. Z kolei, aby coś odczytać, będziemy korzystać z dobrodziejstw klasy ifstream (input file streamstrumień wejścia pliku)

#include <iostream>
#include <fstream>
using namespace std;

ifstream odczyt("plik.txt");
if(!odczyt) //gdy brak miejsca/plik nie istnieje itp.
{
    cout << "Blad otwarcia pliku!" << endl;
    exit(1);
}
//pozostałe operacje na pliku
odczyt.close(); //nie zapominajmy o zamknięciu pliku!
Pamiętajmy, że możemy otwierać tylko te pliki, które istnieją na dysku!

Wczytywanie do zmiennych za pomocą operatora >>

Utwórz nowy plik tekstowy liczby.txt i zamieść w nim dwie liczby: 1920 1080 (oddzielone spacją!!). Zapisz plik w folderze, w którym znajdują się pliki źrodłowe projektu. Przyjrzyjmy się teraz poniższemu przykładowi:

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
	int a; //typ integer, ponieważ zakładamy, że wczytujemy wyłącznie liczby!
	ifstream odczyt("liczby.txt");
	if(!odczyt)
	{
		cout << "Blad otwarcia pliku!" << endl;
		exit(1);
	}
	odczyt >> a; //zauważmy analogię do wczytywania za pomocą cin >>
	cout << a << endl;
	odczyt.close();
	return 0;
}
Zanim przejdziesz do dalszego opisu, domyśl się, co się wyświetli w linijce #15, a następnie skompiluj kod.

Ojej, a czemu nie wyświetliło obu liczb? Pamiętasz jeszcze, że cin >> wczytywało dane do zmiennej do momentu napotkania tzw. białego znaku? Tutaj jest tak samo.

Owszem, możemy poprawić kod w następujący sposób:

int a, b;
odczyt >> a >> b;
cout << a << b << endl;

Ale powyższe rozwiązanie nadaje się tylko do plików z dwiema liczbami. Co zatem w przypadku, kiedy liczb mamy duuużo więcej (i tak właściwie nie wiemy, ile ich jest)?

Odczytywanie pliku za pomocą pętli while

Zanim rozwiążemy zagadkę z poprzedniego podrozdziału, musimy się dowiedzieć co nieco o budowie pliku tekstowego.

W takim typie pliku możemy przechowywać dane będące w postaci alfanumerycznej, tzn. litery, liczby oraz te wszelkie znaczki, które widzisz na swojej klawiaturze. Są w nim zawarte również białe znaki (spacja, tabulacja czy znak nowej linii).

Na końcu każdego pliku tekstowego znajduje się specjalny znacznik (EOFend of file – ang. koniec pliku), dzięki któremu wiadomo, że dane kończą się w tym konkretnym miejscu. Osiągnięcie końca pliku możemy sprawdzić za pomocą metody eof(). Używa się jej najczęściej w pętli while.

Ściągnij plik tekstowy: wczytywane.txt (kliknij prawym przyciskiem myszy, wybierz Zapisz element docelowy jako i ustal ścieżkę dostępu do folderu, w którym znajdują się pliki źródłowe projektu)

	int a, n=0, suma=0;
	ifstream odczyt("wczytywane.txt");
	if(!odczyt)
	{
		cout << "Blad otwarcia pliku!" << endl;
		exit(1);
	}

	while(!odczyt.eof()) //dopóki nie natrafimy na koniec pliku
	{
		odczyt >> a;
		suma+=a;
		n++;
	}
	odczyt.close();
	cout << "Srednia arymetyczna podanych liczb to: " << suma/n << endl;

Pobieranie pojedynczych znaków

Pamiętasz metodę put() do umieszczania w pliku pojedynczych znaków? Analogicznie do "wyciągania" z pliku kolejnych znaków służy metoda get(). Naturalnie, jej użycie nie jest skompilowane:

char znak;
ifstream odczyt("plik.txt");
odczyt.get(znak); //w ten sposób wpisujemy wartość do zmiennej "znak"

Zapisz w odpowiednim miejscu plik tekst.txt.

Napisz program, który:

  • zlicza łaczną liczbę znaków w podanym pliku tekstowym i wyświetla tę wartość w konsoli.

  • zlicza liczbę liter w podanym pliku tekstowym i tę wartość zapisuje w pliku wynik.txt

Wskazówka: do sprawdzania, czy podany znak jest literą, przyda się funkcja isalpha():

char znak;
cin >> znak;
if(isalpha(znak)) cout << "Litera!";

Mamy do dyspozycji również funkcje, które sprawdzają, czy podany znak jest liczbą

isdigit()

lub znakiem alfanumerycznym

isalnum()

Ich użycie jest analogicznie jak w przypadku isalpha()

Wczytywanie wierszy z pliku

Niee, nie mam na myśli żadnych poematów ;-) Chodzi o kolejne linie tekstu, które znajdują się w pliku. Przy okazji omawiania napisów wspominałam o funkcji getline(), dzięki której można było w konsoli wczytać całą linię tekstu aż do napotkania określonego znaku.

Wczytywać wiersze możemy oczywiście do obiektu string bądź łańcucha znaków w postaci tablicy char. Ta druga możliwość ma wadę: należy "przewidzieć" największą liczbę znaków, która może pojawić się w wierszu. W przeciwnym przypadku możemy nie wczytać wszystkiego.

Przypomnijmy sobie, jak korzystamy z funkcji getline():

string s;
getline(odczyt, s); //do napotkania znaku końca linii...
//...lub...
getline(odczyt, s, '*'); //do napotkania określonego znaku

Pamiętaj o załączeniu biblioteki <string>!

Napisz program, który obliczy liczbę linii w tekście w pliku linie.txt oraz wyświetli zawartość pliku w konsoli.

Podsumowanie

Jak widzisz, korzystanie z plików wcale skomplikowane nie jest, a być może przyda się Tobie ta wiedza jeszcze nie raz.

Na koniec zadanko:

Utwórz program, który zapisuje do pliku tabliczkę mnożenia 10x10. Tabelka powinna być ładnie sformatowana (skorzystaj z biblioteki iomanip).
CC By 3.0
Copyright © 2011-2016 by Katarzyna Fokow [Last update: 2017-02-01 17:17:11]
This work is licensed under a Creative Commons Attribution 3.0 Unported License