📚 Hub Books: Онлайн-чтение книгРазная литератураC++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

C++17 STL Стандартная библиотека шаблонов - Яцек Галовиц

Шрифт:

-
+

Интервал:

-
+
1 ... 61 62 63 64 65 66 67 68 69 ... 121
Перейти на страницу:
того, чтобы ее анализировать. Экземпляр класса std::istringstream можно создать на основе существующей строки, которая при необходимости преобразуется в переменные других типов. Если мы хотим объединить обе возможности, то нам подойдет класс std::stringstream.

Удаляем пробелы из начала и конца строк

Полученные из пользовательского ввода строки зачастую содержат лишние пробелы. В одном из предыдущих примеров мы удаляли повторяющиеся пробелы между словами.

Взглянем на пробелы, расположенные вокруг строк, и удалим их. Класс std::string содержит удобные вспомогательные функции, которые нам помогут.

 

 После рассмотрения данного примера, в котором показано, как работать с простыми строковыми объектами, взгляните также и на следующий пример. Там вы увидите, как избежать ненужного копирования или изменения данных при работе с классом std::string_view.

Как это делается

В этом примере мы напишем вспомогательную функцию, которая определяет наличие пробелов в начале и конце строки и возвращает ее копию, но уже без таких пробелов. Затем немного протестируем ее.

1. Как и обычно, сначала идут заголовочные файлы и директива using:

#include <iostream>

#include <string>

#include <algorithm>

#include <cctype>

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[] {" tn"};

  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 {" tn string surrounded by ugly"

            " whitespace tn "};

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 "), то она вернет значение 5, поскольку символ 'a' — первый символ, который не входит в строку "bfo". Порядок символов в строке-аргументе неважен.

Существуют подобные функции с инвертированной логикой, однако мы не использовали их в этом примере:

string::find_first_of and string::find_last_of

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

На основе позиций символов, полученных из этих функций, мы в рамках вспомогательной функции создаем подстроку, не содержащую пробелов в начале и конце, с помощью функции string::substring. Она принимает относительное смещение и длину строки, а затем возвращает новый экземпляр строки, для которого выделен собственный фрагмент памяти, содержащий только эту подстроку. Например, вызов string{"abcdef"}.substr(2, 2) вернет новую строку "cd".

Преимущества использования std::string без затрат на создание объектов std::string

 Класс std::string очень полезен, поскольку значительно упрощает работу со строками. Его недостаток заключается в том, что при необходимости передать подстроку нужно передавать указатель и переменную, содержащую длину подстроки, два итератора или копию подстроки. Мы делали это в предыдущем примере, когда удаляли лишние пробелы из строки, вернув копию подстроки, которая не содержит их.

Если мы хотим передать строку или подстроку в библиотеку, которая не предоставляет поддержку класса std::string, то можем передать только необработанный указатель на строку, что несколько разочаровывает, поскольку этот способ использовался еще во времена С. Как и в случае с выделением подстроки, необработанный указатель не несет информации о длине строки. Таким образом, кто-то должен будет реализовать связку указателя и длины строки.

Говоря упрощенно, такой конструкцией как раз и является класс std::string_view. Он доступен, начиная с версии C++17, и предоставляет способ объединения указателя на некую строку и ее размера. Он воплощает идею наличия ссылочного типа для массивов данных.

Представим, что разрабатываем функции, которые ранее в качестве параметров принимали объекты типа std::string, но при этом не изменяли их так, чтобы экземплярам класса string потребовалось повторно выделять память, содержащую реальные данные. Мы могли бы использовать тип std::string_view для повышения совместимости с библиотеками, которые не знают об STL. Можно позволить другим библиотекам предоставлять string_view для строк, содержащих полезные данные, скрытые за сложной реализацией типа string, а затем применять их в нашем коде, совместимом с STL. Таким образом, класс string_view ведет себя как минималистичный и полезный интерфейс, пригодный для использования многими библиотеками.

Еще одной приятной особенностью является

1 ... 61 62 63 64 65 66 67 68 69 ... 121
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?