Урок №208. Рядкові класи std::string і std::wstring

  Юрій  | 

  Оновл. 20 Кві 2021  | 

 23

Стандартна бібліотека C++ містить багато корисних класів, одним з таких є std::string. std::stringstd::wstring) — це рядковий клас, який дозволяє виконувати операції присвоювання, порівняння і модифікації рядків. На наступних декількох уроках ми докладно розглянемо рядкові класи Стандартної бібліотеки С++.

Примітка: Рядки C-style зазвичай називають «рядками C-style», тоді як std::string (і std::wstring) зазвичай називають просто «рядками».

Навіщо потрібен std::string?

Ми вже знаємо, що рядки C-style використовують масиви типу char для зберігання цілого рядка. Якщо ви спробуєте що-небудь зробити з рядками C-style, то ви дуже швидко виявите, що працювати з ними важко, заплутатися легко, а відлагоджувати — складно.

Рядки C-style мають багато недоліків, в першу чергу пов’язаних з тим, що ви повинні самостійно управляти пам’яттю. Наприклад, якщо ви захочете помістити рядок Hello! в буфер, то вам спочатку потрібно буде динамічно виділити буфер правильної довжини:

Не забудьте врахувати додатковий символ для нуль-термінатора! Потім вам потрібно буде скопіювати значення:

І тут вам не можна помилитися з довжиною буфера, інакше відбудеться переповнення! І, звичайно, оскільки рядок виділяється динамічно, то ви повинні його ще й коректно видалити:

Не забувайте використовувати форму оператора 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:

Насправді в заголовку є 3 різних рядкових класи. Перший — це шаблон класу з ім’ям basic_string<>, який є батьківським класом:

Ви не працюватимете з цим класом напряму, тому не турбуйтеся про те, що таке traits чи Allocator. Значень за замовчуванням, які присвоюються цим об’єктам, буде достатньо майже у всіх мислимих і немислимих випадках.

Далі йдуть два різновиди basic_string<>:

Це ті два класи, які ви будете використовувати. 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.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (Немає Оцінок)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *