Przygotowanie i podstawy
Na początek używamy tylko zmiennych typu string, wejścia z klawiatury, indeksów, pętli i warunków. Dopiero w kolejnej części dodajemy obsługę plików.
Napisy w C++ – string
Do przechowywania tekstu w C++ wykorzystujemy typ
string z biblioteki <string>.
String można traktować jako ciąg znaków, do których dostęp uzyskujemy
podobnie jak do elementów tablicy. W przeciwieństwie do zwykłej tablicy
znaków char, napis typu string może
automatycznie zmieniać swoją długość.
Przykładowa deklaracja napisu:
#include <iostream>
#include <string>
using namespace std;
int main() {
string tekst = "Ala ma kota";
cout << tekst << endl;
}
Dostęp do poszczególnych znaków
Każdy znak w stringu ma swój indeks. Numerowanie zaczyna się od
0, tak jak w tablicach.
string tekst = "kot";
cout << tekst[0] << endl; // k
cout << tekst[1] << endl; // o
cout << tekst[2] << endl; // t
Możemy również wykorzystać pętlę do przejścia przez wszystkie znaki stringu:
string tekst = "kot";
for (int i = 0; i < tekst.length(); i++) {
cout << tekst[i] << endl;
}
Dzięki indeksowaniu można odczytywać, porównywać i zmieniać poszczególne znaki. Należy jednak pamiętać, że indeks musi wskazywać istniejący znak. Dostęp do ostatniego znaku wymaga, aby string nie był pusty. Do napisów typu string w C++ można dołączać inne stringi za pomocą operatora + lub +=, podobnie jak w Pythonie.
Najważniejsze metody i operacje na stringach
| Metoda lub operacja | Zastosowanie | Przykład |
|---|---|---|
length() |
Zwraca długość stringu. | tekst.length() |
size() |
Również zwraca długość stringu. | tekst.size() |
empty() |
Sprawdza, czy string jest pusty. | tekst.empty() |
at(i) |
Zwraca znak o indeksie i i sprawdza, czy indeks jest poprawny. |
tekst.at(0) |
find() |
Wyszukuje znak lub fragment tekstu i zwraca indeks jego pierwszego wystąpienia. | tekst.find("kot") |
substr() |
Pobiera fragment stringu. | tekst.substr(0, 3) |
Przykład wykorzystania kilku metod:
string tekst = "Ala ma kota";
cout << "Dlugosc: " << tekst.length() << endl;
cout << "Pierwszy znak: " << tekst.at(0) << endl;
cout << "Fragment: " << tekst.substr(0, 3) << endl;
cout << "Pozycja slowa: " << tekst.find("kota") << endl;
tekst += "!";
cout << tekst << endl;
find()
Jeżeli wyszukiwany fragment nie występuje w napisie, metoda
find() zwraca string::npos.
Można to wykorzystać do sprawdzenia, czy dany fragment został znaleziony.
if (tekst.find("kot") != string::npos) {
cout << "Znaleziono slowo.";
}
Wczytywanie stringów z klawiatury
Do wczytywania tekstu możemy wykorzystać operator >>
lub funkcję getline(). Różnią się one sposobem traktowania
spacji.
Sposób A – operator >>
string tekst;
cin >> tekst;
Operator >> wczytuje tekst tylko do pierwszego białego
znaku, np. spacji.
Sposób B – funkcja getline()
string tekst;
getline(cin, tekst);
Funkcja getline() wczytuje cały wiersz, łącznie ze spacjami,
aż do końca linii. Jest więc przydatna, gdy użytkownik podaje zdanie
lub dłuższy tekst.
string można indeksować
podobnie jak tablicę, ale nie jest on klasyczną tablicą znaków.
Ponadto w przypadku tekstu zapisanego w UTF-8 polskie litery, takie jak
„ą” lub „ł”, mogą zajmować więcej niż jeden bajt. Metody
length() i indeksowanie nie zawsze odpowiadają więc liczbie
widocznych znaków.
Jak wczytać string?
#include <iostream>
#include <string>
using namespace std;
string tekst;
getline(cin, tekst); // wczytuje całą linię, również spacje
// cin >> tekst; // wczytuje tylko do pierwszej spacji
getline(cin, tekst). Instrukcja cin >> tekst jest odpowiednia, gdy wystarczy jedno słowo.Zadanie 1. Odwracanie napisu – dane z klawiatury
Wczytaj tekst z klawiatury i wypisz znaki w odwrotnej kolejności.
Przykład
Dane wejścioweZadanie 2. Liczenie wystąpień znaku – dane z klawiatury
Wczytaj napis i wybrany znak, a potem policz, ile razy ten znak pojawia się w napisie.
Przykład
DaneRóżne sposoby odczytywania znaków z pliku
W C++ można odczytywać plik znak po znaku, linia po linii albo słowo po słowie. Wybór sposobu ma znaczenie: niektóre metody zachowują spacje i nowe linie, a inne je pomijają.
Do odczytywania danych z plików tekstowych wykorzystujemy klasę ifstream z biblioteki <fstream>.
Przykłady poniżej zakładają, że plik tekst.txt został już otwarty do odczytu:
#include <fstream>
ifstream wejscie("tekst.txt");
Sposób 1. wejscie.get(znak) — każdy znak, również spacje
Metoda get(znak) odczytuje jeden znak i zapisuje go w zmiennej typu char. Odczytuje także spacje, tabulatory oraz znaki nowej linii.
char znak;
while (wejscie.get(znak)) {
cout << znak;
}
Kiedy używać? Przy odwracaniu całej zawartości pliku albo liczeniu wystąpień wybranego znaku, także gdy tym znakiem jest spacja.
Sposób 2. wejscie >> znak — odczyt znaków z pominięciem białych znaków
Operator >> pomija białe znaki, czyli spacje, tabulatory i znaki nowej linii. Dla tekstu Ala ma kota kolejne odczytane znaki to A, l, a, m, a, k i tak dalej — bez spacji.
char znak;
while (wejscie >> znak) {
cout << znak;
}
Kiedy używać? Gdy chcesz sprawdzać litery i inne niebiałe znaki, a spacje i podziały linii nie są istotne. Nie używaj tej metody do odtwarzania oryginalnego tekstu.
Sposób 3. getline() i indeksowanie — linia po linii
getline(wejscie, linia) odczytuje jedną linię do zmiennej typu string. Potem możesz użyć indeksu, aby uzyskać każdy znak tej linii.
string linia;
while (getline(wejscie, linia)) {
for (int i = 0; i < linia.size(); i++) {
cout << linia[i];
}
cout << '\n';
}
getline(wejscie, linia)pobiera kolejną linię.linia.size()zwraca długość napisu.linia[i]daje znak o indeksiei, numerowanie zaczyna się od zera.
Uwaga: znak końca linii nie trafia do zmiennej linia. Dlatego przykład wypisuje '\n' po przetworzeniu każdej linii.
Kiedy używać? Gdy tekst ma być analizowany wierszami albo chcesz przetworzyć każdą linię osobno.
Sposób 4. wejscie >> slowo, a potem slowo[i] — słowo po słowie
Operator >> może wczytać całe słowo do zmiennej typu string. Później możesz przejść po wszystkich znakach tego słowa.
string slowo;
while (wejscie >> slowo) {
for (int i = 0; i < slowo.size(); i++) {
cout << slowo[i] << '\n';
}
}
Spacje i nowe linie rozdzielające słowa są pomijane. Znaki interpunkcyjne, na przykład kropka na końcu kota., pozostają częścią słowa.
Kiedy używać? Przy liczeniu słów, szukaniu najdłuższego słowa i analizowaniu liter należących do poszczególnych wyrazów.
char i są przeznaczone do ćwiczeń na podstawowym alfabecie łacińskim. Polskie znaki zapisane w UTF-8 mogą zajmować kilka bajtów, więc proste operacje znak po znaku nie zawsze odpowiadają pojedynczym widocznym literom.Zadanie 3. Odwrócenie zawartości pliku – odczyt z pliku
Wczytaj dane z pliku tekst.txt, odwróć kolejność znaków i wypisz nową kolejność.
Jak zapisywać dane do pliku?
Do zapisywania danych w plikach tekstowych wykorzystujemy klasę
ofstream z biblioteki <fstream>.
Podstawowe elementy obsługi plików:
| Element | Zastosowanie |
|---|---|
ofstream |
Otwieranie pliku do zapisu |
open() |
Otwieranie pliku |
is_open() |
Sprawdzanie, czy plik został otwarty |
<< |
Zapisywanie tekstu i wartości do pliku |
close() |
Zamykanie pliku |
Plik zapisujemy podobnie jak wypisujemy tekst na ekran za pomocą
cout. Zamiast cout używamy jednak obiektu
reprezentującego plik.
Przykład zapisu na ekranie:
cout << "To jest przykladowy tekst.";
Zapis tego samego tekstu do pliku:
ofstream plik("wynik.txt");
// ofstream plik; //alternatywna wersja
// plik.open("wynik.txt");
if (!plik.is_open()) {
cout << "Nie mozna otworzyc pliku.";
return 1;
}
plik << "To jest przykladowy tekst.";
plik.close();
Po uruchomieniu programu w odpowiednim katalogu pojawi się plik
wynik.txt zawierający zapisany tekst.
Zapisywanie tekstu znak po znaku Operator << pozwala zapisać cały napis za jednym razem. Możemy również zapisywać jego kolejne znaki w pętli. Przykład:
string tekst = "Ala ma kota.";
ofstream plik("wynik.txt");
for (int i = 0; i < tekst.length(); i++) {
plik.put(tekst[i]);
}
plik.close();
Metoda put() zapisuje pojedynczy znak do pliku.
Zadanie 4. Analiza pliku z danymi Pythia
Plik pythia.txt zawiera dane o cząstkach wygenerowanych przez program Pythia. Każdy wiersz opisuje jedną cząstkę i zawiera między innymi jej numer, identyfikator, nazwę, status oraz parametry fizyczne.
Program powinien:
- Odczytać dane z pliku pythia.txt.
- Policzyć liczbę protonów. Proton ma identyfikator
id = 2212. - Znajdź najczęściej i najrzadziej występujący typ cząstki, korzystając z jej identyfikatora
id. - Obliczyć średnią liczbę cząstek przypadających na jedno zdarzenie (event).
- Zapisać wyniki do pliku
statystyki.txt.
Nowe zdarzenie rozpoczyna się od wiersza, w którym no = 0.
Wiersz ten opisuje układ całego zdarzenia ((system)) i nie
należy go liczyć jako cząstki. Liczbę cząstek w zdarzeniu określamy,
zliczając kolejne wiersze, dla których no > 0.
Przykład fragmentu pliku pythia.txt
stringstream
Obiekt stringstream pozwala odczytywać dane z napisu
tak, jakby były pobierane z klawiatury za pomocą operatora
>>. Jest przydatny, gdy chcemy podzielić wiersz
tekstu na poszczególne pola.
#include <sstream>
string linia = "Adam Nowak 22";
stringstream dane(linia);
string Imie;
string Nazwisko;
int Wiek;
dane >> Imie >> Nazwisko >> Wiek;
Po wykonaniu kodu zmienne będą zawierały kolejno wartości
Adam, Nowak i 22. Operator >> automatycznie
pomija spacje i przechodzi do następnego pola.
Wskazówka: W połączeniu z
getline(plik, linia) możesz najpierw odczytać cały
wiersz z pliku, a następnie rozdzielić go na zmienne o odpowiednich
typach za pomocą stringstream.
Przykładowy raport – statystyki.txt
Uwaga: powyższe statystyki dotyczą wyłącznie pokazanego fragmentu danych. Wyniki dla pełnego pliku mogą być inne.
break
Instrukcja break natychmiast przerywa wykonywanie
najbliższej pętli for lub while.
Program przechodzi wtedy do instrukcji znajdującej się za pętlą.
Przykład – odczyt pliku do pierwszego znaku końca linii:
char znak;
while (plik.get(znak)) {
if (znak == '\n') {
break;
}
cout << znak;
}
Program odczytuje i wypisuje znaki aż do napotkania '\n'.
Instrukcja break kończy wtedy pętlę, więc pozostała
część pliku nie jest już odczytywana.
Zadanie dodatkowe. Liczenie znaków w pliku – odczyt i zapis do pliku
Policz liczbę znaków alfanumerycznych w całej zawartości pliku tekst.txt. Zapisz otrzymaną wartość w nowym pliku.
isalnum()
Do sprawdzania, czy znak jest literą lub cyfrą, możesz wykorzystać
funkcję isalnum() z biblioteki <cctype>.
Funkcja zwraca wartość różną od zera, jeśli znak jest literą alfabetu
angielskiego lub cyfrą.
#include <cctype>
if (isalnum(static_cast<unsigned char>(znak))) {
licznik++;
}
W przykładzie licznik zwiększa się tylko wtedy, gdy odczytany znak jest literą lub cyfrą. Spacje, znaki interpunkcyjne i znaki nowej linii nie są zliczane.
Uwaga: standardowa funkcja isalnum()
nie rozpoznaje automatycznie polskich liter zapisanych w kodowaniu
UTF-8. Obsługa takich znaków wymaga dodatkowego rozwiązania.
Przykład
Dane – plik tekst.txt
Ala ma kota 123!
Wynik – plik liczba_znakow.txt
Liczba znakow alfanumerycznych: 12
Najważniejsze polecenia i metody
| Element | Do czego służy? | Przykład |
|---|---|---|
string | Przechowuje tekst. | string tekst; |
getline(cin, tekst) | Wczytuje całą linię z klawiatury. | getline(cin, tekst); |
tekst.size() | Zwraca liczbę elementów napisu. | tekst.size() |
tekst[i] | Pobiera znak o indeksie i. | cout << tekst[i]; |
ifstream | Otwiera strumień do odczytu z pliku. | ifstream wejscie("tekst.txt"); |
ofstream | Otwiera strumień do zapisu do pliku. | ofstream raport("statystyki.txt"); |
wejscie.get(znak) | Odczytuje kolejny znak, także spację i nową linię. | while (wejscie.get(znak)) |
wejscie >> znak | Odczytuje znak, pomijając białe znaki. | while (wejscie >> znak) |
getline(wejscie, linia) | Odczytuje całą linię bez separatora końca linii. | while (getline(wejscie, linia)) |
wejscie.get() | Zwraca kolejny znak jako int, a koniec pliku jako EOF. | kod = wejscie.get() |
wejscie.peek() | Sprawdza następny znak bez pobierania go. | wejscie.peek() |
wejscie >> slowo | Odczytuje kolejne słowo oddzielone białymi znakami. | while (wejscie >> slowo) |
is_open() | Sprawdza, czy plik został otwarty. | if (!wejscie.is_open()) |
close() | Zamyka plik. | wejscie.close(); |
ios::app | Otwiera plik do dopisywania na końcu. | ofstream raport("statystyki.txt", ios::app); |
<< | Zapisuje tekst do strumienia lub pliku. | raport << "Liczba slow"; |
Punktacja – 0–5 pkt
| Punkty | Kryterium |
|---|---|
| 1.0 pkt | Zadanie 1 – Poprawne wczytanie tekstu z klawiatury i wypisanie znaków w odwrotnej kolejności. |
| 0.5 pkt | Zadanie 2 – Wczytanie tekstu i szukanego znaku oraz poprawne policzenie liczby jego wystąpień w tekście. |
| 0.5 pkt |
Zadanie 3 - Odczytanie zawartości pliku tekst.txt, odwrócenie
kolejności znaków i wypisanie nowej kolejności.
|
| 0.5 pkt | Zadanie 4.1 – Odczytać dane z pliku pythia.txt. |
| 0.5 pkt | Zadanie 4.2 – Policzyć liczbę protonów. |
| 1.0 pkt | Zadanie 4.3 – Znajdź najczęściej i najrzadziej występujący typ cząstki. |
| 0.5 pkt | Zadanie 4.4 – Obliczyć średnią liczbę cząstek przypadających na jedno zdarzenie (event). |
| 0.5 pkt | Zadanie 4.5 – Zapisać wyniki do pliku statystyki.txt. |