Язык программирования C++.

9.5. Дополнительные операции со строками.

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

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

9.5.1. Дополнительные способы создания строк. 9.5. Дополнительные операции со строками. Глава 9. Последовательные контейнеры. Часть II. Библиотека С++ Язык программирования C++.9.5.1. Дополнительные способы создания строк.

В дополнение к конструкторам, описанным в разделе 3.2.1, и конструкторам, общим для всех последовательных контейнеров (см. табл. 9.3), тип string предоставляет еще три конструктора, описанные в табл. 9.11.

Таблица 9.11. Дополнительные способы создания строк 

Все значения n, len2 и pos2 являются беззнаковыми.
string s(cp, n); Строка s — копия первых n символов из массива, на который указывает cp. У того массива должно быть по крайней мере n символов
string s(s2, pos2); Строка s — копия символов из строки s2, начиная с позиции по индексу pos2. Если pos2 > s2.size(), результат непредсказуем
string s(s2, pos2, len2); Строка s — копия len2 символов из строки s2, начиная с позиции по индексу pos2. Если pos2 > s2.size(), то результат непредсказуем. Независимо от значения len2, копируется по крайней мере s2.size() - pos2 символов

Конструкторы, получающие тип string или const char*, получают дополнительные (необязательные) аргументы, позволяющие задать количество копируемых символов. При передаче строки можно также указать индекс начала копирования:

const char *cp = "Hello World!!!"; // массив с нулевым символом в конце

char noNull[] = {'H', 'i' };       // без нулевого символа в конце

string s1(cp); // копирует cp до нулевого символа;

               // s1 == "Hello World!!!"

string s2(noNull,2);  // копирует два символа из noNull; s2 == "Hi"

string s3(noNull);    // непредсказуемо: noNull не завершается null

string s4(cp + 6, 5); // копирует 5 символов, начиная с cp[6];

                      // s4 == "World"

string s5(s1, 6, 5);  // копирует 5 символов, начиная с s1[6];

                      // s5 == "World"

string s6(s1, 6);     // копирует от s1[6] до конца s1;

                      // s6 == "World!!!"

string s7(s1, 6, 20); // ok, копирует только до конца s1;

                      // s7 == "World!!!"

string s8(s1, 16);    // передает исключение out_of_range

Обычно строка создается из типа const char*. Массив, на который указывает указатель, должен завершаться нулевым символом; символы копируются до нулевого символа. Если передается также количество, массив не обязан заканчиваться нулевым символом. Если количество не указано и нет нулевого символа или если указанное количество больше размера массива, результат непредсказуем.

При копировании из строки можно предоставить необязательный параметр исходной позиции и количество копируемых символов. Исходная позиция должна быть меньше или равна размеру переданной строки. Если исходная позиция больше размера, то конструктор передаст исключение out_of_range (см. раздел 5.6). При передаче количества копирование начинается с указанной позиции. Независимо от количества запрошенных символов, копирование осуществляется до размера строки, но не более.

Функция substr().

Функция substr() (представленная в табл. 9.12) возвращает копию части или всей исходной строки. Ей можно передать (необязательно) начальную позицию и количество копируемых символов:

string s("hello world");

string s2 = s.substr(0, 5);  // s2 = hello

string s3 = s.substr(6);     // s3 = world

string s4 = s.substr(6, 11); // s3 = world

string s5 = s.substr(12);    // передает исключение out_of_range

Если начальная позиция превышает размер исходной строки, функция substr() передает исключение out_of_range (см. раздел 5.6). Если начальная позиция в сумме с количеством копируемых символов превосходит размер строки, то копирование осуществляется только до конца строки.

Таблица 9.12. Функция substr()

s.substr(pos, n) Возвращает строку, содержащую n символов из строки s, начиная с позиции pos. По умолчанию параметр pos имеет значение 0. Параметр n по умолчанию имеет значение, подразумевающее копирование всех символов строки s, начиная с позиции pos

Упражнения раздела 9.5.1.

Упражнение 9.41. Напишите программу, инициализирующую строку из вектора vector<char>.

Упражнение 9.42. Учитывая, что символы в строку следует читать по одному и заранее известно, что прочитать предстоит по крайней мере 100 символов, как можно было бы улучшить производительность программы?

9.5.2. Другие способы изменения строки. 9.5. Дополнительные операции со строками. Глава 9. Последовательные контейнеры. Часть II. Библиотека С++ Язык программирования C++.9.5.2. Другие способы изменения строки.

Тип string поддерживает операторы присвоения последовательного контейнера, а также функции assign(), insert() и erase() (см. раздел 9.2.5, раздел 9.3.1 и раздел 9.3.3). В нем также определены дополнительные версии функций insert() и erase().

В дополнение к обычным версиям функций insert() и erase(), которые получают итераторы, тип string предоставляет их версии, получающие индекс. Индекс указывает начальный элемент для функции erase() или начальную позицию для функции insert():

s.insert(s.size(), 5, '!'); // вставить пять восклицательных знаков

                            // в конец строки s

s.erase(s.size() - 5, 5);   // удалить последние пять символов из

                            // строки s

Библиотека string предоставляет также версии функций insert() и assign(), получающие массивы символов в стиле С. Например, символьный массив с нулевым символом в конце можно использовать как значение, передаваемое функциям insert() и assign():

const char *cp = "Stately, plump Buck";

s.assign(cp, 7);            // s == "Stately"

s.insert(s.size(), cp + 7); // s == "Stately, plump Buck"

Сначала содержимое строки s заменяется при вызове функции assign(). Присваиваемые строке s символы — это семь символов из начала массива, на который указывает указатель cp. Количество запрашиваемых символов должно быть меньше или равно количеству символов массива (исключая завершающий нулевой символ), на который указывает cp.

Когда происходит вызов функции insert() для строки s, подразумевается вставка символов перед несуществующим элементом в позиции s[size()]. В данном случае копируются символы, начиная с седьмого символа cp и до завершающего нулевого символа.

Символы для функций insert() и assign() можно также указать как исходящие из другой строки или ее подстроки:

string s = "some string", s2 = "some other string";

s.insert(0, s2); // вставить копию s2 перед позицией 0 в s

// вставить s2.size() символов из s2,

// начиная с позиции s2[0] перед s[0]

s.insert(0, s2, 0, s2.size());

Функции append() и replace().

Класс string определяет две дополнительные функции-члена, append() и replace(), способные изменить содержимое строки. Все эти функции описаны в табл. 9.13. Функция append() — упрощенный способ вставки в конец:

string s("C++ Primer"), s2 = s; // инициализация строк s и s2

                                // текстом "С++ Primer"

s.insert(s.size(), " 4th Ed."); // s == "С++ Primer 4th Ed."

s2.append(" 4th Ed."); // эквивалент: добавление " 4th Ed." к s2;

                       // s == s2

Функция replace() — упрощенный способ вызова функций erase() и insert():

// эквивалентный способ замены "4th" на "5th"

s.erase(11, 3); // s == "С++ Primer Ed."

s.insert(11, "5th"); // s == "С++ Primer 5th Ed."

// начиная с позиции 11, удалить три символа, а затем вставить "5th"

s2.replace(11, 3, "5th"); // эквивалент: s == s2

В вызове функции replace() вставляемый текст может быть того же размера, что и удаляемый. Но можно вставить большую или меньшую строку:

s.replace(11, 3, "Fifth"); // s == "С++ Primer Fifth Ed."

В этом вызове удаляются три символа, но вместо них вставляются пять.

Таблица 9.13. Функции изменения содержимого строки

s.insert(pos, args) Вставка символов, определенных аргументом args, перед позицией pos. Позиция pos может быть задана индексом или итератором. Версии, получающие индекс, возвращают ссылку на строку s, а получающие итератор возвращают итератор, обозначающий первый вставленный символ
s.erase(pos, len) Удаляет len символов, начиная с позиции pos. Если аргумент len пропущен, удаляет символы от позиции pos до конца строки s. Возвращает ссылку на строку s
s.assign(args) Заменяет символы строки s согласно аргументу args. Возвращает ссылку на строку s
s.append(args) Добавляет аргумент args к строке s. Возвращает ссылку на строку s
s.replace(range, args) Удаляет диапазон range символов из строки s и заменяет их символами, заданными аргументом args. Диапазон задан либо индексом и длиной, либо парой итераторов. Возвращает ссылку на строку s
Аргументы args могут быть одним из следующих: функции append() и assign() могут использовать все формы. Строка str должна быть отлична от s, а итераторы b и е не могут принадлежать строке s
str Строка str
str, pos, len До len символов из строки str, начиная с позиции pos
cp, len До len символов из символьного массива, на который указывает указатель cp
cp Завершающийся нулевым символом массив, на который указывает указатель cp
n, c n копий символа с
b, e Символы в диапазоне, указанном итераторами b и е
Список инициализации Разделяемый запятыми список символов, заключенный в фигурные скобки
Аргументы args для функций replace() и insert() зависят от того, использован ли диапазон или позиция
replace(pos,len,args) replace(b,е,args) insert(pos,args) insert(iter,args) Возможные аргументы args
Да Да Да Нет str
Да Нет Да Нет str, pos, len
Да Да Да Нет cp, len
Да Да Нет Нет cp
Да Да Да Да n, с
Нет Да Нет Да b2, e2
Нет Да Нет Да список инициализации

Множество способов изменить строку.

Функции append(), assign(), insert() и replace(), перечисленные в табл. 9.13, имеют несколько перегруженных версий. Аргументы этих функций зависят от того, как заданы добавляемые символы и какая часть строки изменится. К счастью, у этих функций общий интерфейс.

У функций assign() и append() нет необходимости определять изменяемые части строки: функция assign() всегда заменяет все содержимое строки, а функция append() всегда добавляет в конец строки.

Функция replace() предоставляет два способа определения диапазона удаления символов. Диапазон можно определить по позиции и длине или парой итераторов. Функция insert() предоставляет два способа определения позиции вставки: при помощи индекса или итератора. В любом случае новый элемент (элементы) вставляется перед указанным индексом или итератором.

Существует несколько способов определения символов, добавляемых в строку. Новые символы могут быть взяты из другой строки, из указателя на символ, из заключенного в фигурные скобки списка символов или как символ и как число. Когда символы исходят из строки или указателя на символ, можно передать дополнительные аргументы, указывающие, копируются ли все символы аргумента или только часть.

Не каждая функция поддерживает все версии этих аргументов. Например, нет версии функции insert(), получающей индекс и список инициализации. Аналогично, если необходимо определить точку вставки, используя итератор, невозможно будет впоследствии передать символьный указатель как источник для новых символов.

Упражнения раздела 9.5.2.

Упражнение 9.43. Напишите функцию, получающую три строки: s, oldVal и newVal. Используя итераторы, а также функции insert(), и erase() замените все присутствующие в строке s экземпляры строки oldVal строкой newVal. Проверьте функцию на примере замены таких общепринятых сокращений, как "tho" на "though" и "thru" на "through".

Упражнение 9.44. Перепишите предыдущую функцию так, чтобы использовались индекс и функция replace().

Упражнение 9.45. Напишите функцию, получающую строку, представляющую имя и две другие строки, представляющие префикс, такой, как "Mr." или "Ms.", а также суффикс, такой, как "Jr." или "III". Используя итераторы, а также функции insert() и append(), создайте новую строку с суффиксом и префиксом, добавленным к имени.

Упражнение 9.46. Перепишите предыдущее упражнение, используя на сей раз позицию, длину и функцию insert().

9.5.3. Операции поиска строк. 9.5. Дополнительные операции со строками. Глава 9. Последовательные контейнеры. Часть II. Библиотека С++ Язык программирования C++.9.5.3. Операции поиска строк.

Класс string предоставляет шесть вариантов функций поиска, у каждой из которых есть четыре перегруженных версии. Функции-члены поиска и их аргументы описаны в табл. 9.14. Каждая из них возвращает значение типа string::size_type, являющееся индексом найденного элемента. Если соответствие не найдено, функции возвращают статический член (см. раздел 7.6) по имени string::npos. Библиотека определяет значение npos как -1 типа const string::size_type. Поскольку npos имеет беззнаковый тип, это означает, что значение npos соответствует наибольшему возможному размеру, который может иметь любая строка (см. раздел 2.1.2).

Таблица 9.14. Строковые функции поиска

Функции поиска возвращают индекс искомого символа или значение npos, если искомое не найдено
s.find(args) Ищет первое местоположение аргумента args в строке s
s.rfind(args) Ищет последнее местоположение аргумента args в строке s
s.find_first_of(args) Ищет первое местоположение любого символа аргумента args в строке s
s.find_last_of(args) Ищет последнее местоположение любого символа аргумента args в строке s
s.find_first_not_of(args) Ищет первое местоположение символа в строке s, который отсутствует в аргументе args
s.find_last_not_of(args) Ищет последнее местоположение символа в строке s, который отсутствует в аргументе args
Аргумент args может быть следующим
с, pos Поиск символа с, начиная с позиции pos в строке s. По умолчанию pos имеет значение 0
s2, pos Поиск строки s2, начиная с позиции pos в строке s. По умолчанию pos имеет значение 0
cp, pos Поиск строки с завершающим нулевым символом в стиле С, на которую указывает указатель cp. Поиск начинается с позиции pos в строке s. По умолчанию pos имеет значение 0
cp, pos, n Поиск первых n символов в массиве, на который указывает указатель cp. Поиск начинается с позиции pos в строке s. Аргумент pos и n не имеет значения по умолчанию

Язык программирования C++.Функции поиска строк возвращают значение беззнакового типа string::size_type. Поэтому не следует использовать переменную типа int или другого знакового типа для содержания значения, возвращаемого этими функциями (см. раздел 2.1.2).

Самой простой является функция find(). Она ищет первое местоположение переданного аргумента и возвращает его индекс или значение npos, если соответствующее значение не найдено:

string name("AnnaBelle");

auto pos1 = name.find("Anna"); // pos1 == 0

Возвращает значение 0, т.е. индекс, по которому подстрока "Anna" расположена в строке "AnnaBelle".

Поиск (и другие операции со строками) чувствительны к регистру. При поиске в строке регистр имеет значение:

string lowercase("annabelle");

pos1 = lowercase.find("Anna"); // pos1 == npos

Этот код присвоит переменной pos1 значение npos, поскольку строка "Anna" не соответствует строке "anna".

Немного сложней искать соответствие любому символу в строке. Например, следующий код находит первую цифру в переменной name:

string numbers("0123456789"), name("r2d2");

// возвращает 1, т.е. индекс первой цифры в имени

auto pos = name.find_first_of(numbers);

Кроме поиска соответствия, вызвав функцию find_first_not_of(), можно искать первую позицию, которая не соответствует искомому аргументу. Например, для поиска первого нечислового символа в строке можно использовать следующий код:

string dept("03714p3");

// возвращает 5 - индекс символа 'p'

auto pos = dept.find_first_not_of(numbers);

Откуда начинать поиск.

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

string::size_type pos = 0;

// каждая итерация находит следующее число в имени

while ((pos = name.find_first_of(numbers, pos))

        != string::npos) {

 cout << "found number at index: " << pos

      << " element is " << name[pos] << endl;

 ++pos; // перевести на следующий символ

}

Условие цикла while присваивает переменной pos индекс первой встретившейся цифры, начиная с текущей позиции pos. Пока функция find_first_of() возвращает допустимый индекс, результат отображается, а значение pos увеличивается.

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

Поиск в обратном направлении.

Использованные до сих пор функции поиска выполняется слева направо (т.е. от начала к концу). Библиотека предоставляет аналогичный набор функций, которые просматривают строку справа налево (т.е. от конца к началу). Функция-член rfind() ищет последнюю, т.е. расположенную справа, позицию искомой подстроки.

string river("Mississippi");

auto first_pos = river.find("is"); // возвращает 1

auto last_pos = river.rfind("is"); // возвращает 4

Функция find() возвращает индекс 1, указывая, что подстрока "is" первый раз встречается, начиная с позиции 1, а функция rfind() возвращает индекс 4, указывая начало последнего местонахождения подстроки "is".

Функция find_last() аналогична функции find_first(), но возвращает последнее местоположение, а не первое.

• Функция find_last_of() ищет последний символ, который соответствует любому элементу искомой строки.

• Функция find_last_not_of() ищет последний символ, который не соответствует ни одному элементу искомой строки.

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

Упражнения раздела 9.5.3.

Упражнение 9.47. Напишите программу, которая находит в строке "ab2c3d7R4E6" каждую цифру, а затем каждую букву. Напишите две версии программы: с использованием функции find_first_of() и функции find_first_not_of().

Упражнение 9.48. С учетом определения переменных name = "r2d2" и numbers = "0123456789", что возвращает вызов функции numbers.find(name)?

Упражнение 9.49. У символов может быть надстрочная часть, расположенная выше середины строки, как у d или f, или подстрочная, ниже середины строки, как у p или g. Напишите программу, которая читает содержащий слова файл и сообщает самое длинное слово, не содержащее ни надстрочных, ни подстрочных элементов.

9.5.4. Сравнение строк. 9.5. Дополнительные операции со строками. Глава 9. Последовательные контейнеры. Часть II. Библиотека С++ Язык программирования C++.9.5.4. Сравнение строк.

Кроме операторов сравнения (см. раздел 3.2.2), библиотека string предоставляет набор функций сравнения, подобных функции strcmp() библиотеки С (см. раздел 3.5.4). Подобно функции strcmp(), функция s.compare() возвращает нуль, положительное или отрицательное значение, в зависимости от того, равна ли строка s, больше или меньше строки, переданной ее аргументом.

Как показано в таб. 9.15, существует шесть версий функции compare(). Ее аргументы зависят от того, сравниваются ли две строки или строка и символьный массив. В обоих случаях сравнивать можно либо всю строку, либо ее часть.

Таблица 9.15. Возможные аргументы функции s.compare()

s2 Сравнивает строку s со строкой s2
pos1, n1, s2 Сравнивает n1 символов, начиная с позиции pos1 из строки s, со строкой s2
pos1, n1, s2, pos2, n2 Сравнивает n1 символов, начиная с позиции pos1 из строки s, со строкой s2, начиная с позиции pos2 в строке s2
cp Сравнивает строку s с завершаемым нулевым символом массивом, на который указывает указатель cp
pos1, n1, cp Сравнивает n1 символов, начиная с позиции pos1 из строки s, со строкой cp
pos1, n1, cp, n2 Сравнивает n1 символов, начиная с позиции pos1 из строки s, со строкой cp, начиная с символа n2 

9.5.5. Числовые преобразования. 9.5. Дополнительные операции со строками. Глава 9. Последовательные контейнеры. Часть II. Библиотека С++ Язык программирования C++.9.5.5. Числовые преобразования.

Строки зачастую содержат символы, которые представляют числа. Например, числовое значение 15 можно представить как строку с двумя символами, '1' и '5'. На самом деле символьное представление числа отличается от его числового значения. Числовое значение 15, хранимое в 16-разрядной переменной типа short, будет иметь двоичное значение 0000000000001111, а символьная строка "15", представленная как два символа из набора Latin-1, будет иметь двоичное значение 0011000100110101. Первый байт представляет символ '1', восьмеричное значение которого составит 061, а второй байт, представляющий символ '5', в наборе Latin-1 имеет восьмеричное значение 065.

Язык программирования C++.Новый стандарт вводит несколько функций, осуществляющих преобразование между числовыми данными и библиотечным типом string.

Таблица 9.16. Преобразования между строками и числами

to_string(val) Перегруженные версии функции возвращают строковое представление значения val. Аргумент val может иметь любой арифметический тип (см. раздел 2.1.1). Есть версии функции to_string() для любого типа с плавающей точкой и целочисленного типа, включая тип int и большие типы. Малые целочисленные типы преобразуются, как обычно (см. раздел 4.11.1)
stoi(s, p, b) stol(s, p, b) stoul(s, p, b) stoll(s, p, b) stoull(s, p, b) Возвращают числовое содержимое исходной подстроки s как тип  int, long, unsigned long, long long или unsigned long long соответственно. Аргумент b задает используемое для преобразования основание числа; по умолчанию принято значение 10. Аргумент p — указатель на тип size_t, означающий индекс первого нечислового символа в строке s; по умолчанию p имеет значение 0. В этом случае функция не хранит индекс
stof(s, p) stod(s, p) stold(s, p) Возвращают числовое содержимое исходной подстроки s как тип float, double или long double соответственно. Аргумент p имеет то же назначение, что и у целочисленных преобразований

int i = 42;

string s = to_string(i); // преобразует переменную i типа int в ее

                         // символьное представление

double d = stod(s);      // преобразует строку s в значение типа double

Здесь для преобразования числа 42 в его строковое представление используется вызов функции to_string(), а затем вызов функции stod() преобразует эту строку в значение с плавающей точкой.

Первый преобразуемый в числовое значение символ строки должен быть цифрой:

string s2 = "pi = 3.14";

// d = 3.14 преобразуется первая подстрока в строке s, начинающаяся

// с цифры; d = 3.14

d = stod(s2.substr(s2.find_first_of("+-.0123456789")));

Для получения позиции первого символа строки s, который мог быть частью числа, в этом вызове функции stod() используется функция find_first_of() (см. раздел 9.5.3). Функции stod() передается подстрока строки s, начиная с этой позиции. Функция stod() читает переданную строку до тех пор, пока не встретится символ, который не может быть частью числа. Затем найденное символьное представление числа преобразуется в соответствующее значение типа double.

Первый преобразуемый символ строки должен быть знаком + или - либо цифрой. Строка может начаться с части 0x или 0X, означающей шестнадцатеричный формат. У функций преобразования чисел с плавающей точкой строка может также начинаться с десятичной точки ( .) и содержать символ е или E, означающий экспоненциальную часть. У функций преобразования в целочисленный тип, в зависимости от основания, строка может содержать алфавитные символы, соответствующие цифрам после цифры 9.

Язык программирования C++.Если строка не может быть преобразована в число, эти функции передают исключение invalid_argument (см. раздел 5.6). Если преобразование создает значение, которое не может быть представлено заданным типом, они передают исключение out_of_range.

Упражнения раздела 9.5.5.

Упражнение 9.50. Напишите программу обработки вектора vector<string>, элементы которого представляют целочисленные значения. Вычислите сумму всех элементов вектора. Измените программу так, чтобы она суммировала строки, которые представляют значения с плавающей точкой.

Упражнение 9.51. Напишите класс, у которого есть три беззнаковых члена, представляющих год, месяц и день. Напишите конструктор, получающий строку, представляющую дату. Конструктор должен понимать множество форматов даты, такие как January 1,1900, 1/1/1900, Jan 1,1900 и т.д.