CodeGym /Kursy /C++ SELF /Stringi — std::string, konkatenacja, długość

Stringi — std::string, konkatenacja, długość

C++ SELF
Poziom 2, Lekcja 2
Dostępny

1. Wprowadzenie

Kiedy dopiero zaczynasz programować, łatwo wpaść w iluzję, że „najważniejsze są liczby”. Rzeczywiście: oceny, pieniądze, wiek, liczniki — wszędzie cyfry. Ale gdy zechcesz uczynić program choć trochę przyjaznym dla człowieka, nagle okazuje się, że człowiek komunikuje się słowami. Imię, nazwisko, miasto, komentarz do zamówienia, tytuł filmu — to wszystko tekst, czyli string.

Kiedy C++ powstał, nie było w nim specjalnego typu dla stringów (string) — string traktowano po prostu jako ciąg znaków (typ char). Jednak z czasem okazało się, że to niewygodne, i zdecydowano o wprowadzeniu odrębnego typu string oraz dodaniu go do standardowej (STD) biblioteki C++. Tak powstał w C++ typ std::string.

Można go traktować jak kontener sekwencji znaków: wewnątrz są litery, spacje, znaki interpunkcyjne. I co ważne, standardowa biblioteka daje gotowe operacje na stringach: łączenie, poznawanie długości, dostęp do znaków po indeksie.

Aby przećwiczyć pracę ze std, będziemy rozwijać jedno małe aplikacyjne demo w trakcie kursu: „konsolowa wizytówka”. Na razie będzie po prostu składać ładny string wizytówki i wypisywać go. Dzisiaj dodamy imię i kilka tekstowych detali.

2. Pierwszy string: dołączamy <string>

Teraz moment, który na początku wydaje się drobiazgiem, a potem staje się drugą naturą: aby używać std::string, zwykle trzeba dołączyć nagłówek <string>. Tak, w niektórych kompilatorach „jakoś działa bez niego”, ale to zły nawyk: lepiej dołączać to, z czego korzystasz.

Zacznijmy od prostego: utworzymy string z literału znakowego "..." i wypiszemy go.

#include <iostream>
#include <string>

int main() {
    std::string course = "C++ for beginners";
    std::cout << course << '\n'; // C++ for beginners
}

Tu ważna idea: std::string to typ, jak int, tylko dla tekstu. Zadeklarowaliśmy zmienną course i włożyliśmy w nią tekst. Potem zachowuje się jak wartość, którą można przekazać do wypisu.

String "A" i znak 'A'

W programowaniu jest dowcip: „dwa cudzysłowy decydują o losie projektu”. I niestety to prawie prawda. W C++ są literały łańcuchowe w podwójnych cudzysłowach "..." i literały znakowe w pojedynczych '...'. Różnica jest zasadnicza: string to sekwencja znaków, a znak to dokładnie jeden znak.

Jeśli potrzebujesz przechować imię "Ada", to jest string. Jeśli potrzebujesz jednego znaku, np. pierwszej litery imienia 'A', to jest znak. std::string przechowuje string, a typ dla pojedynczego znaku to formalnie char, o którym powiemy później.

Porównaj:

#include <iostream>
#include <string>

int main() {
    std::string word = "Cat";                   // string tekstu
    char firstLetter = 'C';                     // jeden znak

    std::cout << word << '\n';        // Cat
    std::cout << firstLetter << '\n'; // C
}

Na pierwszy rzut oka podobne, ale sens inny: word może mieć dowolną długość, a firstLetter to dokładnie jeden znak.

3. Konkatenacja: + i +=

Przechodzimy do bardzo „ludzkiej” operacji: złożenia z kawałków jednej ładnej frazy. W stringach nazywa się to konkatenacją. Wyobraź sobie, że string to konstruktor LEGO, tylko zamiast klocków masz słowa i spacje. Składasz zdanie, które pokażesz użytkownikowi.

Są dwa podstawowe sposoby łączenia:

  • a + b tworzy nowy string (jakbyś wziął dwa arkusze i zrobił trzeci, gdzie jest wszystko razem).
  • a += b dopisuje do istniejącego stringa (jakbyś kontynuował pisanie na tym samym arkuszu).

Zacznijmy od +:

#include <iostream>
#include <string>

int main() {
    std::string first = "Ada";
    std::string last = "Lovelace";

    std::string full = first + " " + last;
    std::cout << full << '\n'; // Ada Lovelace
}

Zwróć uwagę na " ". Spacja też jest znakiem i często ma znaczenie. Jeśli zapomnisz spacji, powstanie AdaLovelace, co wygląda jak nazwa klasy, a nie imię i nazwisko.

Teraz wariant z += — wygodny, gdy budujesz string krok po kroku:

#include <iostream>
#include <string>

int main() {
    std::string badge = "Name: ";
    badge += "Ada";
    badge += " ";
    badge += "Lovelace";

    std::cout << badge << '\n'; // Name: Ada Lovelace
}

Pod względem czytelności przypomina „składanie stringa krok po kroku”. W przyszłości, gdy pojawią się warunki i pętle, += będzie szczególnie przydatne, ale już teraz dobrze pokazuje ideę „dopisania”.

Mała uwaga: string i liczby nie da się po prostu „dodawać” jak stringi. Na przykład "Age: " + 10 — to nie zadziała, bo to różne typy. Nie będziemy teraz wnikać w konwersje; zapamiętaj: stringi łączy się ze stringami.

4. Długość stringa: size()

Kiedy trzymasz string, często chcesz wiedzieć, ile ma znaków. Dla tego std::string ma metodę size() (czasem spotyka się też length(), ale dziś wystarczy size()). To jak prędkościomierz pokazujący „ile znaków jest teraz w stringu”.

Zobaczmy w praktyce:

#include <iostream>
#include <string>

int main() {
    std::string city = "Boston";
    std::cout << city.size() << '\n'; // 6
}

Warto przyzwyczaić się: size() to pierwsza rzecz, o której myślisz, zanim zaczniesz indeksować. Bo indeksowanie bez rozumienia długości — to jak iść po schodach po omacku, zakładając, że stopnie są nieskończone. Spoiler: stopnie są skończone, a kolano jedno.

Kolejny przykład, gdzie długość pokazuje, że spacja też się liczy:

#include <iostream>
#include <string>

int main() {
    std::string s = "Hi Bob";
    std::cout << s.size() << '\n'; // 6  (H i _ B o b)
}

5. Indeksowanie: s[i]

Jak już ustaliliśmy — string to zestaw znaków. A skoro to zestaw, można pobierać pojedyncze znaki po ich numerze.

Ważne!

W programowaniu numeracja zwykle zaczyna się od zera. Numeracja od zera oznacza: pierwszy znak to s[0], drugi — s[1], i tak dalej.

Wyobraź sobie string jako pudełko z przegródkami:

flowchart LR
    A["s = 'c a t'"] --> B["indeksy: 0 1 2"]

Spójrzmy na kod:

#include <iostream>
#include <string>

int main() {
    std::string animal = "cat";

    std::cout << animal[0] << '\n'; // c
    std::cout << animal[1] << '\n'; // a
    std::cout << animal[2] << '\n'; // t
}

O granicach

Aby się mniej mylić, w programowaniu wygodniej używać terminu indeks zamiast numer. Indeks zawsze zaczyna się od zera.

Indeks powinien zawierać się w przedziale od 0 do s.size() - 1. Jeśli string ma długość 3, dopuszczalne indeksy to 0, 1, 2. Jeśli spróbujesz pobrać animal[3], wychodzisz poza granice i program może się zawiesić.

W C++ jest to niebezpieczne, bo program może zachowywać się nieprzewidywalnie: czasem „jakby działa”, czasem wypisuje dziwne znaki, czasem pada. I to jest przypadek, gdy „czasem działa” jest gorsze niż „od razu pada”, bo błąd ukrywa się i czeka na odpowiedni moment.

Czy można zmieniać znaki w stringu?

Tak, znak o danym indeksie można zamienić: string jest mutowalny, co przydaje się do prostych modyfikacji.

#include <iostream>
#include <string>

int main() {
    std::string word = "bat";
    word[0] = 'c';

    std::cout << word << '\n'; // cat
}

Zwróć uwagę: po prawej stoi 'c' w pojedynczych cudzysłowach, bo przypisujemy jeden znak, a nie string "c".

6. Pierwsze spotkanie z metodami

Mogłeś zauważyć dziwny zapis:

city.size()

Dlaczego nie piszemy po prostu size(city)? Bo size() to metoda.

Metoda to funkcja należąca do konkretnego typu lub obiektu. W naszym przypadku std::string to typ, a city — obiekt tego typu. Stringi mają swoje „wbudowane umiejętności”: poznawać długość, dopisywać tekst, pobierać znak po indeksie i wiele innych.

Gdy funkcja należy do obiektu, wywołujemy ją przez kropkę:

obiekt.metoda()

Kropka czyta się niemal po ludzku: „Weź obiekt city i wywołaj na nim metodę size()”.

Czyli string „wie sam”, jak obliczyć swoją długość. To jest idea metod: zachowanie związane z danymi.

Spójrz na znane już przykłady:

std::string full = first + " " + last;

std::cout << full.size() << '\n';
std::cout << full[0] << '\n';

full.size() — string sam podaje swoją długość. full[0] — odwołujemy się do jego wewnętrznego znaku.

Później zobaczysz, że prawie wszystkie złożone typy w C++ mają swoje metody. Na przykład std::vector ma push_back(), std::string ma substr(), strumienie mają clear(). To jeden z kluczowych zasad języka: dane i operacje na nich są powiązane.

Warto przyzwyczaić się do takiego zapisu. Wszystko, co wygląda jak:

coś.coś()

prawie zawsze oznacza wywołanie metody obiektu.

Dlatego piszemy full.size(), a nie po prostu size(full). String udostępnia narzędzia do pracy ze sobą.

7. Mini-aplikacja: konsolowa wizytówka

Teraz zbierzemy wszystko w jeden mały fragment: zrobimy wizytówkę, która wypisze imię, długość pełnego imienia i pierwszą literę. Na razie dane weźmiemy bezpośrednio z kodu (wejście pojawi się w kolejnym wykładzie), aby nie mieszać nowych tematów.

W tym przykładzie uzyskasz poczucie „aplikacji”: nie tylko pojedyncze operacje, ale mały użyteczny wypis.

#include <iostream>
#include <string>

int main() {
    std::string first = "Ada";
    std::string last = "Lovelace";

    std::string full = first + " " + last;

    std::cout << "Full name: " << full << '\n';          // Full name: Ada Lovelace
    std::cout << "Length: " << full.size() << '\n';      // Length: 12
    std::cout << "First letter: " << full[0] << '\n';    // First letter: A
}

Tu ukrytych jest kilka ważnych nawyków.

Po pierwsze, wyraźnie dodaliśmy spację między imieniem a nazwiskiem, bo komputer nie domyśli się, że „między słowami powinno być miejsce”. Po drugie, wypisaliśmy długość stringa: to pomaga zrozumieć, że spacja też się liczy. Po trzecie, pobraliśmy full[0] i wydobyliśmy pierwszy znak, co daje w przyszłości możliwość tworzenia inicjałów czy prostych skrótów.

Jeśli chcesz trochę „pobawić się”, możesz pobrać ostatnią literę nazwiska. Formalnie ostatnia litera ma indeks size() - 1. Robimy to tylko dla niepustego stringa, by nie ryzykować wyjścia poza granice:

#include <iostream>
#include <string>

int main() {
    std::string last = "Lovelace";
    char lastChar = last[last.size() - 1];

    std::cout << lastChar << '\n'; // e
}

Tak, wyrażenie wygląda nieco „matematycznie”, ale logika prosta: jeśli długość to 8, indeksy idą 0…7, zatem ostatni to 7, czyli size() - 1.

Przypomnienie operacji na stringach

By nie trzymać wszystkiego w głowie naraz, warto mieć małą „mapę”. Poniżej krótka tabela z tym, czego dziś używaliśmy (bez zagłębiania się w bardziej zaawansowane metody stringów — będą później).

Operacja Przykład Co się dzieje
Utworzyć string
std::string s = "Hi";
W s przechowywany jest tekst
Połączyć stringi
a + b
Tworzony jest nowy string
Dopisać do stringa
s += part;
s staje się dłuższy
Długość stringa
s.size()
Otrzymujemy liczbę znaków
Znak po indeksie
s[i]
Pobieramy jeden znak (albo możemy go zamienić)

Ta tabela to Twój „minimalny zestaw”, przydatny w prawie każdym zadaniu konsolowym: od wypisywania powitania po wstępną obróbkę tekstu.

8. Typowe błędy przy pracy ze std::string

Błąd nr 1: zapomnieli #include <string>.
Czasem kod „jakby” się kompiluje przez pośrednie dołączenia, a czasem niespodziewanie przestaje. Dobre nawyki są proste: używasz std::string — dołącz <string>. To nie kaprys, to dyscyplina: nawet w przykładach i dokumentach zdarzają się poprawki, które dodają brakujący include, bo bez niego przykład jest niepoprawny.

Błąd nr 2: pomylili "A" i 'A'.
Gdy piszesz word[0] = "b";, kompilator słusznie powie: „hej, po lewej jeden znak, a po prawej string”. Dla jednego znaku użyj pojedynczych cudzysłowów: 'b'. Dla tekstu — podwójnych: "b".

Błąd nr 3: zapomnieli o spacji przy konkatenacji.
Klasyka: first + last daje „AdaLovelace”, a ty potem długo patrzysz i myślisz, że program się zepsuł. W rzeczywistości jest zbyt dosłowny: nie poprosiłeś o spację — nie dodał. Dodawaj " " explicite.

Błąd nr 4: indeks poza granicami stringa.
s[100] dla stringa "Hi" — to nie „pustka”, nie null i nie „nic”. To wyjście poza granice i zachowanie programu staje się nieprzewidywalne. Zanim pojawi się if, nie robiemy losowych indeksów i zawsze opieramy się na size().

Błąd nr 5: oczekiwano, że size() to „liczba liter w dowolnym języku”.
Na początku kursu pracujemy zwykle na prostych przykładach łacińskich, gdzie jeden znak to mniej więcej jedna „litera”. W prawdziwych tekstach (zwłaszcza z znakami wielobajtowymi w UTF‑8) sprawa może być trudniejsza, ale nasza obecna rola to opanowanie podstaw mechaniki stringów, nie rozwiązywanie wszystkich problemów kodowania znaków.

1
Zadanie
C++ SELF, poziom 2, lekcja 2
Niedostępne
Plakat filmowy
Plakat filmowy
1
Zadanie
C++ SELF, poziom 2, lekcja 2
Niedostępne
Mini wizytówka
Mini wizytówka
1
Zadanie
C++ SELF, poziom 2, lekcja 2
Niedostępne
Pierwszy i ostatni
Pierwszy i ostatni
1
Zadanie
C++ SELF, poziom 2, lekcja 2
Niedostępne
Popraw login
Popraw login
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION