Стандартна бібліотека C++ містить багато корисних класів, одним з яких є std::string. std::string (і std::wstring) — це рядковий клас, який дозволяє виконувати операції присвоювання, порівняння і модифікації рядків. На наступних декількох уроках ми детально розглянемо рядкові класи Стандартної бібліотеки С++.
Примітка: Рядки C-style зазвичай називають «рядками C-style», тоді як std::string (і std::wstring) зазвичай називають просто «рядками».
Навіщо потрібен std::string?
Ми вже знаємо, що рядки C-style використовують масиви типу char для зберігання цілого рядка. Якщо ви спробуєте що-небудь зробити з рядками C-style, то ви дуже швидко виявите, що працювати з ними важко, заплутатися легко, а відлагоджувати — складно.
Рядки C-style мають багато недоліків, в першу чергу пов’язаних з тим, що ви повинні самостійно управляти пам’яттю. Наприклад, якщо ви захочете помістити рядок Hello!
в буфер, то вам спочатку потрібно буде динамічно виділити буфер правильної довжини:
1 |
char *strHello = new char[7]; |
Не забудьте врахувати додатковий символ для нуль-термінатора! Потім вам потрібно буде скопіювати значення:
1 |
strcpy(strHello, "Hello!"); |
І тут вам не можна помилитися з довжиною буфера, інакше відбудеться переповнення! І, звичайно, оскільки рядок виділяється динамічно, ви повинні його ще й коректно видалити:
1 |
delete[] strHello; |
Не забувайте використовувати форму оператора delete, яка працює з масивами, а не звичайну форму оператора delete.
Крім того, більшість з інтуїтивно зрозумілих операторів, які надає мова C++ для роботи з числами, такі як =
, ==
, !=
, <
, >
, >=
і <=
просто не працюють з рядками C-style. Іноді вони можуть працювати без помилок (з боку компілятора), але результат буде невірним. Наприклад, порівняння двох рядків C-style з використанням оператора ==
насправді виконає порівняння вказівників, а не рядків. Присвоювання одного рядка C-style іншому рядку C-style з використанням оператора =
працюватиме, але виконуватиметься копіювання вказівника (поверхневе копіювання), що не завжди те, що нам потрібно. Такі речі можуть легко призвести до помилок і збоїв у програмі, а розбиратися з ними не так вже й легко (відносно)!
Суть в тому, що працюючи з рядками C-style, вам потрібно пам’ятати безліч прискіпливих правил про те, що робити безпечно, а що — ні; запам’ятовувати багато функцій, таких як strcat() і strcmp(), щоб використовувати їх замість інтуїтивних операторів; а також самостійно виконувати управління пам’яттю.
На щастя, мова C++ надає набагато кращий спосіб для роботи з рядками: класи std::string і std::wstring. Використовуючи такі концепції С++, як конструктори, деструктори і перевантаження операторів, std::string дозволяє створювати і маніпулювати рядками в інтуїтивно зрозумілій формі і, що не менш важливо, виконувати це безпечно! Ніякого управління пам’яттю, запам’ятовування дивних назв функцій і значно менша ймовірність виникнення помилок/збоїв.
Клас std::string
Весь функціонал класу std::string знаходиться в заголовку string:
1 |
#include <string> |
Насправді в заголовку є 3 різних рядкових класи. Перший — це шаблон класу з ім’ям basic_string<>
, який є батьківським класом:
1 2 3 4 5 |
namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string; } |
Ви не працюватимете з цим класом напряму, тому не турбуйтеся про те, що таке traits
чи Allocator
. Значень за замовчуванням, які присвоюються цим об’єктам, буде достатньо майже у всіх мислимих і немислимих випадках.
Далі йдуть два різновиди basic_string<>
:
1 2 3 4 5 |
namespace std { typedef basic_string<char> string; typedef basic_string<wchar_t> wstring; } |
Це ті два класи, які ви будете використовувати. std::string використовується для стандартних ASCII-рядків (кодування UTF-8), а std::wstring використовується для Unicode-рядків (кодування UTF-16). Вбудованого класу для рядків UTF-32 немає.
Хоча ви будете напряму використовувати std::string і std::wstring, весь функціонал реалізований в класі basic_string<>
. string і wstring мають доступ до цього функціоналу напряму. Отже, всі функції, наведені нижче, працюють як зі string, так і з wstring.
Функціонал std::string
конструктор — створює або копіює рядок;
деструктор — знищує рядок.
capacity() — повертає кількість символів, які рядок може зберігати без додаткового перевиділення пам’яті;
empty() — повертає логічне значення, яке вказує, чи є рядок порожнім;
length(), size() — повертають кількість символів в рядку;
max_size() — повертає максимальний розмір рядка, який може бути виділений;
reserve() — розширює або зменшує ємність рядка.
[]
, at() — доступ до елементу по заданому індексу.
=
, assign() — присвоюють нове значення рядку;
+=
, append(), push_back() — додають символи до кінця рядка;
insert() — вставляє символи в довільний індекс рядка;
clear() — видаляє всі символи рядка;
erase() — видаляє символи за довільним індексом рядка;
replace() — замінює символи довільних індексів рядка іншими символами;
resize() — розширює або зменшує рядок (видаляє або додає символи в кінці рядка);
swap() — міняє місцями значення двох рядків.
>>
, getline() — зчитують значення з вхідного потоку в рядок;
<<
— записує значення рядка у вихідний потік;
c_str() — конвертує рядок в рядок C-style з нуль-термінатором в кінці;
copy() — копіює вміст рядка (без нуль-термінатора) в масив типу char;
data() — повертає вміст рядка у вигляді масиву типу char, який не закінчується нуль-термінатором.
==
, !=
— порівнюють, чи є два рядки рівними/нерівними (повертають значення типу bool);
<
, <=
, >
, >=
— порівнюють, чи є два рядки менше або більше один одного (повертають значення типу bool);
compare() — порівнює, чи є два рядки рівними/нерівними (повертає -1
, 0
або 1
).
+
— з’єднує два рядки;
substr() — повертає підрядок.
find — шукає індекс першого символу/підрядка;
find_first_of — шукає індекс першого символу з набору символів;
find_first_not_of — шукає індекс першого символу НЕ з набору символів;
find_last_of — шукає індекс останнього символу з набору символів;
find_last_not_of — шукає індекс останнього символу НЕ з набору символів;
rfind — шукає індекс останнього символу/підрядка.
Підтримка ітераторів і розподільників (allocators):
begin(), end() — повертають “прямий” ітератор, який вказує на перший і останній (елемент, який йде за останнім) елементи рядка;
get_allocator() — повертає розподільник;
rbegin(), rend() — повертають “зворотний” ітератор, який вказує на останній (тобто “зворотний” початок) і перший (елемент, який передує першому елементу рядка — “зворотний” кінець) елементи рядка. Відмінність від begin() і end() в тому, що рух ітераторів відбувається в зворотному напрямку.
Висновки
Ось і весь функціонал std::string. Більшість з цих функцій мають кілька різновидів для обробки різних типів вхідних даних, про це ми ще поговоримо.
Хоча функціонал досить широкий, все ж є кілька помітних упущень:
підтримка регулярних виразів;
конструктори для створення рядків з чисел;
прописні/рядкові функції;
токенізація/розбиття рядків на масиви;
прості функції для отримання лівої чи правої частини рядка;
обрізка пробілів;
конвертація з UTF-8 в UTF-16 і навпаки.
Все вищеперераховане вам доведеться реалізувати самостійно, або конвертувати ваш рядок в рядок C-style (використовуючи функцію c_str()) і тоді вже використовувати функціонал, який пропонує мова C++.
Примітка: Хоча у вищенаведених прикладах використовується string, все це також може бути застосовано і до wstring.