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.
