std::cout
, но не выводит данные на консоль. Вместо этого он выводит данные в строковый буфер. После того, как мы поместим все переменные в поток, разделив их пробелами с помощью оператора <<
, можем создать и вывести на экран новый объект строки, задействовав конструкцию o.str()
.
ostringstream o;
o << a << " " << b << " " << c << " " << d;
auto concatenated (o.str());
cout << concatenated << '\n';
7. Мы также можем преобразовать эту новую строку путем, например, перевода всех ее символов в верхний регистр. Библиотечная функция C
toupper
, которая соотносит символы в нижнем регистре и символы в верхнем, оставляя другие символы неизменными, уже доступна, и ее можно объединить с функцией std::transform
, поскольку строка, по сути, представляет собой итерабельный контейнер, содержащий элементы типа char
.
transform(begin(concatenated), end(concatenated),
begin(concatenated), ::toupper);
cout << concatenated << '\n';
}
8. Компиляция и запуск программы дадут следующий результат, который полностью оправдывает наши ожидания:
$ ./creating_strings
a, b
c, d
ab
ac
a b c d
A B C D
Как это работает
Очевидно, строки можно складывать с помощью оператора
+
прямо как числа. Математика здесь не используется, но в итоге мы получаем сконкатенированные строки. Чтобы иметь возможность работать с объектами класса string_view
, сначала нужно выполнить их преобразование к типу std::string
.Однако очень важно отметить: при объединении в коде строк и строковых представлений мы никогда не должны предполагать, что хранящаяся в экземпляре класса
string_view
строка завершается нулевым символом! Именно поэтому мы используем конструкцию "abc"s + string{some_string_view}
вместо конструкции "abc"s + some_string_view.data()
. Кроме того, класс std::string
предоставляет функцию-член append
, которая может работать с экземплярами класса string_view
, но изменяет строку вместо того, чтобы вернуть новую строку, к которой прикреплено содержимое строкового представления.
Класс
std::string_view
очень полезен, но будьте осторожны при смешивании его со строками и строковыми функциями. Нельзя рассчитывать на то, что эти строки будут оканчиваться нулями, поэтому весь порядок быстро нарушается в стандартном строковом окружении. К счастью, зачастую предусмотрены подходящие перегруженные версии функций, которые могут обрабатывать подобные ситуации.
Сложную конкатенацию строк с форматированием и т.д. не следует выполнять шаг за шагом для экземпляров строк. Классы
std::stringstream
, std::ostringstream
и std::istringstream
подходят для этого гораздо лучше, так как позволяют более качественно управлять памятью при объединении строк и предоставляют все возможности по форматированию, характерные для потоков. Для данного раздела мы выбрали класс std::ostringstream
, поскольку собираемся создать строку вместо того, чтобы ее анализировать. Экземпляр класса std::istringstream
можно создать на основе существующей строки, которая при необходимости преобразуется в переменные других типов. Если мы хотим объединить обе возможности, то нам подойдет класс std::stringstream
.Удаляем пробелы из начала и конца строк
Полученные из пользовательского ввода строки зачастую содержат лишние пробелы. В одном из предыдущих примеров мы удаляли повторяющиеся пробелы между словами.
Взглянем на пробелы, расположенные вокруг строк, и удалим их. Класс
std::string
содержит удобные вспомогательные функции, которые нам помогут.
После рассмотрения данного примера, в котором показано, как работать с простыми строковыми объектами, взгляните также и на следующий пример. Там вы увидите, как избежать ненужного копирования или изменения данных при работе с классом
std::string_view
.
Как это делается
В этом примере мы напишем вспомогательную функцию, которая определяет наличие пробелов в начале и конце строки и возвращает ее копию, но уже без таких пробелов. Затем немного протестируем ее.
1. Как и обычно, сначала идут заголовочные файлы и директива
using
:
#include
#include
#include
#include
using namespace std;
2. Наша функция будет принимать константную ссылку на существующую строку. А возвращать станет новую строку, из начала и конца которой удалены лишние пробелы:
string trim_whitespace_surrounding(const string &s)
{
3. Класс
std::string
предоставляет две удобные функции, которые будут очень полезны. Первая функция — это string::find_first_not_of
, она принимает строку, содержащую все символы, которые мы хотим опустить. В нашем случае таковыми являются символы пробела ' '
, табуляции '\t'
и перехода на новую строку '\n'
. Функция возвращает позицию первого символа, не совпадающего с переданными. При наличии в строке только пробелов она вернет значение string::npos
. Это значит следующее: если мы удалим все пробелы, то останется только пустая строка. Так что в подобных случаях просто возвращайте пустую строку:
const char whitespace[] {" \t\n"};
const size_t first (s.find_first_not_of(whitespace));
if (string::npos == first) { return {}; }
4. Теперь мы знаем, где должна начинаться новая строка, но пока неизвестно, где она заканчивается. Поэтому воспользуемся другой полезной функцией строки:
string::find_last_not_of
. Она вернет позицию последнего символа, не являющегося пробелом.
const size_t last (s.find_last_not_of(whitespace));
5. С помощью функции
string::substr
мы теперь можем вернуть часть строки, окруженной пробелами, в которой их не будет. Эта функция принимает два параметра: позицию от начала строки и количество символов после данной позиции.
return s.substr(first, (last - first + 1));
}
6. На этом все. Напишем функцию
main
, в которой создадим строку, окружающую предложение всеми видами пробелов, чтобы обрезать ее:
int main()
{
string s {" \t\n string surrounded by ugly"
" whitespace \t\n "};
7. Выведем на экран необрезанную и обрезанную версии строки. Окружив строку скобками, можно показать все пробелы, которые находились в ней до обрезки.
cout << "{" << s << "}\n";
cout << "{"
<< trim_whitespace_surrounding(s)
<< "}\n";
}
8. Компиляция и запуск программы дадут ожидаемый результат:
$ ./trim_whitespace
{
string surrounded by ugly whitespace
}
{string surrounded by ugly whitespace}
Как это работает
В этом разделе мы применили функции
string::find_first_not_of
и string::find_ last_not_of
. Обе принимают строку, созданную в стиле C, в виде списка символов, которые нужно проигнорировать при поиске другого символа. Если у нас есть экземпляр строки, содержащий строку "foo bar"
, и мы вызовем для него функцию find_first_not_of("bfo ")
, то она вернет значение