Menu

Stringi w C++: podstawy std::string, metody i pułapki

Jak używać std::string w C++: bezpieczne budowanie, łączenie, przeszukiwanie i wycinanie tekstu oraz dlaczego do prawdziwej pracy prawie nigdy nie chcesz surowego char*.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

std::string: tekst, który sam sobą zarządza

Na poprzedniej stronie widać było, jak inteligentne wskaźniki pozwalają obiektowi posiadać zasób i automatycznie po nim sprzątać. std::string to ta sama idea zastosowana do tekstu: posiada bufor znaków, powiększa go, gdy dopisujesz, i zwalnia, gdy napis wychodzi z zasięgu. Nigdy nie wywołujesz new, nie liczysz bajtów i nie martwisz się o brakujący terminator null.

Aby go użyć, dołącz <string>. Klasa należy do przestrzeni nazw std.

Operator + wykonuje tu prawdziwą pracę: alokuje nowy bufor wystarczająco duży na oba kawałki i kopiuje je do niego. Przy surowym char*, czyli zwykłym wskaźniku do bufora znaków, trzeba by sięgać po strcpy i strcat z nadzieją, że bufor jest wystarczająco duży. std::string sprawia, że cała ta klasa błędów znika.

Budowanie i łączenie napisów

Łączyć można przez +, ale podstawowym narzędziem do powiększania napisu w miejscu jest +=. Dopisuje do istniejącego bufora, zamiast za każdym razem tworzyć zupełnie nowy napis, co ma znaczenie w pętlach.

Częste zaskoczenie: + wymaga, żeby przynajmniej jeden operand już był std::string. Dwa literały tekstowe to tylko const char*, więc "a" + "b" się nie kompiluje: nie ma operator+ dla dwóch surowych wskaźników. Najpierw zrób z jednego z nich string:

string s = "a" + "b";              // error: can't add two const char*
string s = string("a") + "b";      // fine - left side is a std::string
string s = "a"s + "b";             // fine in C++14+, the "s" literal suffix

Ostatnia forma używa przyrostka s z <string> (using namespace std::string_literals;), który zamienia literał bezpośrednio w std::string.

Sięganie do wnętrza napisu

std::string zachowuje się jak kontener char, więc możesz go indeksować, przechodzić po nim pętlą i prosić o jego fragmenty.

substr(pos, len) zwraca zupełnie nowy napis skopiowany z oryginału; nie zmienia źródła. Uważaj na granice: word[10] na napisie o 5 znakach to niezdefiniowane zachowanie: nie rzuci wyjątku, tylko przeczyta śmieci. Jeśli chcesz dostępu ze sprawdzaniem, który rzuca std::out_of_range, użyj word.at(10) zamiast word[10].

Kolejna klasyczna pułapka: .size() zwraca typ bez znaku (size_t). Pętla wsteczna w rodzaju for (size_t i = word.size() - 1; i >= 0; --i) nigdy się nie kończy, bo i bez znaku nigdy nie spadnie poniżej zera, tylko przekręci się do ogromnej liczby. Gdy musisz przejść napis od końca, użyj indeksu ze znakiem albo przebuduj warunek.

Wyszukiwanie i zamiana

find odnajduje podnapis albo znak i zwraca indeks początku. Gdy szukanego elementu nie ma, zwraca specjalną stałą std::string::npos: zawsze porównuj z nią, zamiast zakładać, że find zwraca -1.

Aby zmienić tekst w miejscu, replace(pos, len, text) zamienia fragment na nową treść (która może mieć inną długość), a insert/erase dodają albo usuwają kawałki:

Napisy i liczby

Tekst i liczby nie konwertują się same: "42" to trzy znaki, a nie liczba całkowita 42. Biblioteka standardowa daje funkcje konwersji w obie strony. Używaj stoi, stod i pokrewnych do parsowania tekstu na liczby, a to_string do konwersji w drugą stronę.

Są tu dwie pułapki. Po pierwsze stoi("abc") rzuca std::invalid_argument, więc niezaufane dane zabezpieczaj blokiem try/catch. Po drugie to_string(3.99) daje pełne 3.990000: jeśli potrzebujesz kontroli nad precyzją i formatowaniem, to zadanie dla strumieni napisów, do których właśnie zmierzamy.

try {
    int n = std::stoi(userInput);
} catch (const std::invalid_argument&) {
    std::cout << "That wasn't a number.\n";
}

Dalej: wejście i wyjście

Napisy wypisujesz przez cout od samego początku, ale wczytywanie ich od użytkownika ma własne niespodzianki: cin >> name zatrzymuje się na pierwszej spacji, więc cała linia, taka jak „Ada Lovelace”, wymaga zamiast tego std::getline. Następna strona porządnie omawia wejście i wyjście w C++: operatory strumieni, wczytywanie całych linii i łączenie >> z getline bez potykania się o pozostałe znaki nowej linii.

Najczęściej zadawane pytania

Czym się różni std::string od char* w C++?

std::string to klasa, która posiada własną pamięć i nią zarządza, rośnie w razie potrzeby i sprząta po sobie automatycznie. char* to tylko surowy wskaźnik na znaki zakończone '\0': sam zarządzasz buforem, długością i czasem życia. Prawie do wszystkiego wybieraj std::string; usuwa całe kategorie błędów przepełnienia bufora i wiszących wskaźników.

Jak poznać długość stringa w C++?

Wywołaj .size() albo jego alias .length() na std::string: name.size() zwraca liczbę znaków jako size_t. Obie metody zwracają tę samą wartość; size() to częstszy styl, bo pasuje do wszystkich innych kontenerów STL.

Jak zamienić string na liczbę w C++?

Użyj std::stoi dla int, std::stod dla double i podobnych (stol, stof). Przykład: int n = std::stoi("42");. Te funkcje rzucają std::invalid_argument, jeśli tekst nie jest liczbą, więc gdy dane wejściowe nie są zaufane, otocz je blokiem try/catch.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ