22.1 – std::string и std::wstring
Стандартная библиотека C++ содержит множество полезных классов, но, пожалуй, наиболее полезным является std::string
. std::string
(и std::wstring
) – это строковый класс, который предоставляет множество операций для присваивания, сравнения и изменения строк. В данной главе мы подробно рассмотрим эти строковые классы.
Примечание: строки в стиле C мы будем называть «строками в стиле C», тогда как std::string
(и std::wstring
) будем называть просто «строками».
Необходимость в строковом классе
В предыдущем уроке мы рассмотрели строки в стиле C, в которых для хранения строки символов используются массивы char
. Если вы попытаетесь сделать что-нибудь со строками в стиле C, вы очень быстро придете к выводу, что с ними сложно работать, легко испортить и сложно отладить.
Строки в стиле C имеют много недостатков, в первую очередь связанных с тем фактом, что вам придется самостоятельно управлять памятью. Например, если вы захотите присвоить строку "hello!" буферу, вы должны будете сначала динамически выделить память правильной длины для этого буфера:
char *strHello = new char[7];
Не забудьте учесть дополнительный символ для завершающего нуля!
Затем вам нужно будет скопировать в него значение:
strcpy(strHello, "hello!");
Надеюсь, вы сделали буфер достаточно большим, чтобы не было его переполнения!
И, конечно же, поскольку строка размещается динамически, вы должны не забыть правильно удалить ее, когда закончите работу с ней:
delete[] strHello;
Не забывайте использовать удаление для массива вместо обычного удаления!
Более того, многие интуитивно понятные операторы, которые предоставляет C для работы с числами, такие как присваивание и сравнение, просто не работают со строками в стиле C. Иногда может показаться, что они работают, но на самом деле дают неверные результаты – например, сравнение двух строк в стиле C с использованием ==
на самом деле будет сравнивать указатели, а не строки. Присваивание одной строки в стиле C другой с помощью оператора =
поначалу будет казаться, что работает, но на самом деле в этом случае выполняется копирование указателя (поверхностное копирование), что обычно не то, что вам нужно. Подобные вещи могут привести к сбоям программы, которые очень трудно найти и отладить!
Суть в том, что работа со строками в стиле C требует запоминания множества особенностей о том, что безопасно/небезопасно, запоминания набора функций с забавными названиями, такими как strcat()
и strcmp()
, вместо использования интуитивно понятных операторов, и требует много ручного управления памятью.
К счастью, C++ и стандартная библиотека предоставляют гораздо лучший способ работы со строками: классы std::string
и std::wstring
. Используя концепции C++, такие как конструкторы, деструкторы и перегрузка операторов, std::string
позволяет создавать строки и управлять ими интуитивно и безопасно! Больше никакого управления памятью, никаких странных имен функций и гораздо меньшая вероятность сбоя.
Обзор классов строк
Весь строковый функционал стандартной библиотеки находится в заголовочном файле. Чтобы использовать его, просто включите заголовок string
:
#include <string>
На самом деле в заголовке string
есть 3 разных класса строк. Первый – это шаблонный базовый класс с именем basic_string
:
namespace std
{
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string;
}
Вы не будете работать с этим классом напрямую, поэтому пока не беспокойтесь о том, что это за traits
и Allocator
. Значений по умолчанию будет достаточно почти во всех мыслимых случаях.
Стандартная библиотека предоставляет две разновидности basic_string
:
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 нет встроенного класса (хотя вы можете расширить из basic_string
свой собственный класс, если он вам нужен).
Хотя вы будете напрямую использовать std::string
и std::wstring
, весь строковый функционал реализован в классе basic_string
. string
и wstring
могут получить доступ к этому функционалу напрямую благодаря шаблону. Следовательно, все представленные функции будут работать как для string
, так и для wstring
. Однако, поскольку basic_string
является шаблонным классом, это также означает, что, если вы сделаете что-то синтаксически неверное со string
или wstring
, компилятор выдаст ужасно выглядящие ошибки шаблона. Не пугайтесь этих ошибок; они выглядят намного хуже, чем они есть на самом деле!
Вот список всех функций в строковом классе. Для обработки различных типов входных данных большинство этих функций имеют несколько разновидностей, которые мы рассмотрим более подробно в следующих уроках.
Функция | Действие |
---|---|
Создание и уничтожение | |
конструктор | Создает или копирует строку |
деструктор | Уничтожает строку |
Размер и вместимость | |
capacity() | Возвращает количество символов, которые могут храниться без перераспределения памяти |
empty() | Возвращает логическое значение, указывающее, пуста ли строка |
length() , size() | Возвращает количество символов в строке |
max_size() | Возвращает максимальный размер строки, которая может быть размещена |
reserve() | Увеличить или уменьшить вместимость строки |
Доступ к элементам | |
[] , at() | Доступ к символу по определенному индексу |
Модификация | |
= , assign() | Присваивает новое значение строке |
+= , append() , push_back() | Добавляет символы в конец строки |
insert() | Вставляет символы в строку по произвольному индексу |
clear() | Удаляет все символы в строке |
erase() | Стирает символы по произвольному индексу в строке |
replace() | Заменяет символы с произвольным индексом на другие символы |
resize() | Расширение или сжатие строки (обрезает или добавляет символы в конце строки) |
swap() | Меняет местами значения двух строк |
Ввод и вывод | |
>> , getline() | Считывает значения из входного потока в строку |
<< | Записывает значение строки в выходной поток |
c_str() | Возвращает содержимое строки как строку в стиле C с завершающим нулем |
copy() | Копирует содержимое (не оканчивающееся нулем) в массив символов |
data() | То же, что c_str() . Неконстантная перегрузка позволяет выполнять запись в возвращаемую строку |
Сравнение строк | |
== , != | Сравнивает, равны или неравны две строки (возвращает bool ) |
< , <= , > , >= | Сравнивает, являются ли две строки меньше/больше друг друга (возвращает bool ) |
compare() | Сравнивает, равны или неравны две строки (возвращает -1, 0 или 1) |
Подстроки и конкатенация | |
+ | Объединяет две строки |
substr() | Возвращает подстроку |
Поиск | |
find() | Найти индекс первого символа/подстроки |
find_first_of() | Найти индекс первого символа из набора символов |
find_first_not_of() | Найти индекс первого символа, не входящего в набор символов |
find_last_of() | Найти индекс последнего символа из набора символов |
find_last_not_of() | Найти индекс последнего символа, не входящего в набор символов |
rfind() | Найти индекс последнего символа/подстроки |
Поддержка итераторов и распределителей памяти (аллокаторов) | |
begin() , end() | Поддержка итератора прямого направления для начала/конца строки |
get_allocator() | Возвращает распределитель |
rbegin() , rend() | Поддержка итератора обратного направления для начала/конца строки |
Хотя строковые классы библиотеки STL предоставляют множество функций, есть несколько заметных упущений:
- поддержка регулярных выражений;
- конструкторы для создания строк из чисел;
- функции изменения регистра на верхний/нижний;
- сравнение без учета регистра;
- разбиение строки на массив;
- простые функции для получения левой или правой части строки;
- обрезка пробелов в начале и конце строки;
- форматирование строки в стиле
sprintf
; - преобразование из UTF-8 в UTF-16 или наоборот.
Для большинства из них вам придется либо написать свои собственные функции, либо преобразовать вашу строку в строку в стиле C (используя c_str()
) и использовать функции C, которые предлагают эту функциональность.
В следующих уроках мы более подробно рассмотрим различные функции класса string
. Хотя в наших примерах мы будем использовать string
, всё в равной степени применимо и к wstring
.