Перейти к содержимому
Topos Uranos

Краткое содержание

Текст является самым частым видом данных в программе: имена, сообщения, строки файла, команды пользователя. Этот урок рассматривает его таким, каким он является для C++: последовательностью символов с собственными операциями. Он представляет std::string, которую создают, конкатенируют, сравнивают и обходят как вектор char, созданный для работы с текстом; её операции поиска и извлечения (find, substr, starts_with, contains) и значение std::string::npos, которым find сообщает, что ничего не нашла; std::string_view, представление, которое наблюдает текст, не копируя его, и условие, от которого оно зависит: наблюдаемый текст должен продолжать существовать. Завершают урок строки в стиле C, чтобы читать чужой код, и порядок, в котором C++ сравнивает и сортирует слова.

Цели обучения

  1. Использовать операции std::string (size, +, find, substr, starts_with, contains) для решения задач с текстом.
  2. Истолковывать результат find, сравнивать его с std::string::npos и предсказывать, что происходит, если обращаться с ним как с обычным числом.
  3. Принимать текст в функции как std::string_view и объяснять на примере, почему представление не должно пережить строку, которую оно наблюдает.
  4. Читать код со строками в стиле C и объяснять их отличия от std::string: завершающий нулевой символ, фиксированный размер и сумму двух литералов, которая не компилируется.

Строка std::string

Создать, конкатенировать, обойти

Урок Символы, логические значения, преобразования и константы представил символ, char, как небольшое число, которое таблица превращает в букву. Текст есть последовательность символов; если это так, его можно хранить в std::vector<char>, и то, чему урок Векторы научил о последовательностях, было бы верно и для него. Однако от текста требуют операций, которых не требуют от произвольной последовательности: приклеить к нему другой текст, найти в нём слово, извлечь кусок, вывести его целиком. Отсюда следует отдельный тип, строка, std::string, из заголовка <string>: вектор char, созданный для работы с текстом.

Программа на C++
#include <format>#include <print>#include <string>​int main(){    const std::string first{"Ada"};    std::string greeting{"Hola, " + first};    greeting += '.';    std::println("{} ({} символов)", greeting, greeting.size());    std::println("Первый «{}», последний «{}»", greeting[0], greeting.back());    int spaces{0};    for (const char c : greeting) {        if (c == ' ') {            ++spaces;        }    }    std::println("Пробелов: {}", spaces);    const std::string code{std::format("{}-{:03}", "MAT", 7)};    std::println("{} {}", code, code == "MAT-007");}
Вывод
Hola, Ada. (10 символов)Первый «H», последний «.»Пробелов: 1MAT-007 true

В строке 7 из литерала создаётся строковый объект. Строка 8 его конкатенирует с испанским приветствием «Hola, » («Привет, »): оператор + между строкой и литералом даёт новую строку с символами обоих, одними вслед за другими. Строка 9 добавляет в конец символ с помощью +=, который принимает также строку или литерал. Остальное уже известно по векторам: size() считает символы (10, потому что запятая, пробел и точка тоже символы), greeting[0] есть первый символ, а back() последний, и цикл в строках с 13 по 17 обходит строку символ за символом в форме по диапазону из урока Циклы. Как и вектор, строка допускает ещё push_back, pop_back и empty, а библиотека GCC 15 без оптимизации контролирует её индексы в operator[] (с оптимизацией этот контроль исчезает).

Две последние строки выполняют то, что обещал урок Проектирование с помощью функций. std::format из <format> принимает те же заполнители, что и std::println, с теми же спецификациями формата ({:03} дополняет нулями до трёх цифр), но вместо того чтобы вывести результат, возвращает его как строку: так строят текст, который затем сохраняют, сравнивают или передают функции. А == сравнивает две строки по содержимому: они равны, если у них одинаковый размер и одни и те же символы в том же порядке. Следовательно, code == "MAT-007" есть true, хотя текст слева построен по частям, а текст справа является литералом.

Сравнение

Помимо == и !=, строки допускают <, <=, > и >=, которые сравнивают их в лексикографическом порядке, порядке словаря: обе строки проходят параллельно, и в первой позиции, где они различаются, решает меньший символ; если одна заканчивается раньше без всякого различия, меньшей является более короткая. Так, испанское слово «casa» меньше, чем «cosa», потому что 'a' < 'o' во второй позиции, а «mar» меньше, чем «marea», потому что первое является префиксом второго. Эти операторы сравнивают строки; между двумя литералами в кавычках они сравнивают нечто другое, потому что литерал есть не std::string, а массив, как будет видно в параграфе о строках в стиле C. Какой символ «меньше», решает его код, а не алфавит; последний параграф урока показывает, что из этого следует.