Объект-строка string. Операции с объектами класса string

Практический курс по C/C++: https://stepik.org/course/193691

Я думаю, вы помните, насколько неудобно было работать со строками в языке Си? Они описывались одномерным массивом типа char и содержали набор символов той или иной строки. Например, фразу «Hello, World!»:

char msg[] = "Hello, World!";

В конце каждой корректной Си-строки должен присутствовать символ ‘\0’, то есть, просто число 0.

Конечно, в С++ мы можем продолжать использовать этот подход для определения набора символов и работать с ними, как со строками. Но теперь появилась дополнительная возможность применять объект string, который значительно упрощает строковые операции на уровне написания программы программистом. Для этого первым делом необходимо подключить заголовок string в нашей программе следующим образом:

#include <string>

После этого в пространстве имен std будет доступен класс string с возможностью объявления строковых объектов. В самом простом варианте можно записать:

std::string msg;

Так как мы не проходили классы и ООП, то воспринимайте string, как специальный тип данных для объявления строк. В результате, msg будет представлять собой пустую строку, то есть, строку, содержащую только одно значение 0 – маркер конца строки.

В действительности, класс string базируется на динамическом массиве символов, в котором хранится текущая строка. Поэтому, нам не обязательно прописывать максимальную длину строки, это значение будет меняться в соответствии с длиной хранимой строки. Например, для объекта msg можно вывести число символов в строке и физический размер динамического массива:

cout << msg.size() << " " << msg.capacity() << endl;

Получим:

0 15

То есть, размер строки 0, а зарезервировано под строку 15 символов. Если же инициализировать объект-строку какой-либо строкой:

std::string msg {"Hello, Sergey Balakirev!"};
cout << msg.size() << " " << msg.capacity() << endl;

то увидим числа:

24 24

То есть, размер динамического массива автоматически был увеличен до 24 – числа символов в строке.

Саму же строку можно вывести как обычную переменную с помощью объекта cout следующим образом:

cout << msg << endl;

Или же, получить константный указатель на строковый массив символов:

const char* data = msg.data();

и, например, вывести данные с помощью функции printf():

printf("%s\n", data);

Все это еще раз показывает, что внутри объекта msg мы имеем все ту же Си-строку в виде массива символов.

Далее, для доступа к отдельным символам строки можно по-прежнему использовать оператор []. Например:

std::string fio {"Sergey Balakirev!"};
std::string fio_new {fio};
 
fio_new[5] = 'i';
cout << fio_new << endl;

Увидим измененный символ ‘y’:

Sergei Balakirev!

Соответственно, можно прочитать отдельный символ по индексу:

cout << fio_new[0] << endl;

Или, то же самое можно выполнить с помощью метода at():

fio_new.at(5) = 'i';
cout << fio_new << "\n" << fio_new.at(0) << endl;

Объект-строку можно перебрать по символам с помощью цикла for. В самом простом варианте это можно сделать так:

    for(const char& ch : fio)
        cout << ch << " ";

В более сложном, использовать итератор для перебора любых последовательностей, определенных в стандартной библиотеки шаблонов (STL):

    for(auto it = msg.cbegin(); it != msg.cend(); ++it)
        cout << *it << " ";

Я не буду сейчас углубляться в эту тему. Привел этот пример для демонстрации многообразия возможностей языка С++ и библиотеки STL.

Наконец, можно воспользоваться уже известным вам способом перебора Си-строки:

    const char* ptr_str = fio.data();
    for(int i = 0; ptr_str[i] != '\0'; ++i)
        cout << ptr_str[i] << " ";

Конечно, удобнее всего использовать первый способ, поэтому чаще всего он и встречается в программах.

Добавление и объединение строк

Приятной возможностью объекта-строк является возможность неограниченно (пока хватает памяти устройства) добавлять новые символы в конец строки. Например, пусть имеется строка:

std::string msg {"Hello"};

И мы хотим ее преобразовать в строку «Hello, Sergey!», то есть в конец дописать недостающий фрагмент. Это легко сделать с помощью метода append() следующим образом:

msg.append(", Sergey!");
cout << msg << endl;

Также мы можем объединять строки между собой. Например:

std::string msg {"Hello"};
std::string name {"Sergey"};
msg.append(", ");
msg = msg + name;
cout << msg << endl;

Или, короче:

msg += name;

Или, еще короче:

msg = msg.append(", ") + name;

Все эти варианты вполне допустимы.

Ввод строк из стандартного входного потока

Наверное, последний важный момент связан с возможностью ввода строковых данных из стандартного входного потока stdin. Чаще всего – это ввод строк с клавиатуры. Так как в программе у нас имеется объект cin, давайте воспользуемся им и посмотрим, как это будет работать с объектами-строками:

#include <iostream>
#include <string>
 
using std::cout;
using std::cin;
using std::endl;
 
int main()
{
    std::string msg;
    cin >> msg;
    cout << msg << endl;
    return 0;
}

Если ввести строку «hello world», то в объекте msg будет только первый фрагмент «hello». Чтобы прочитать строку целиком (до символа перевода строки) удобно воспользоваться функцией getline() языка С++. Например, так:

getline(cin, msg);

Теперь, при вводе «hello world», вся строка целиком помещается в объект msg.

Третьим аргументом в этой функции можно указать разделитель, до которого будет читаться введенный фрагмент. Например, если прописать:

getline(cin, msg, ';');

то при вводе:

1, 2; 3, 4

в объект msg будет помещена строка:

1, 2

Вот базовые возможности работы с объектами-строками языка С++. Как видите, они несколько удобнее обычных одномерных массивов символов, используемых в языке Си.

Практический курс по C/C++: https://stepik.org/course/193691

Видео по теме