3.2.2. Операции со строками
Наряду с определением способов создания и инициализации объектов класс определяет также операции, которые можно выполнять с объектами класса. Класс может определить обладающие именем операции, такие как функция isbn() класса Sales_item (см. раздел 1.5.2). Класс также может определить то, что означают различные символы операторов, такие как << или +, когда они применяются к объектам класса. Наиболее распространенные операции класса string приведены в табл. 3.2.
Таблица 3.2. Операции класса string
os << s Выводит строку s в поток вывода os. Возвращает поток os is >> s Читает разделенную пробелами строку s из потока is. Возвращает поток is getline(is, s) Читает строку ввода из потока is в переменную s. Возвращает поток is s.empty() Возвращает значение true, если строка s пуста. В противном случае возвращает значение false s.size() Возвращает количество символов в строке s s[n] Возвращает ссылку на символ в позиции n строки s; позиции отсчитываются от 0 s1 + s2 Возвращает строку, состоящую из содержимого строк s1 и s2 s1 = s2 Заменяет символы строки s1 копией содержимого строки s2 s1 == s2 s1 != s2 Строки s1 и s2 равны, если содержат одинаковые символы. Регистр символов учитывается <, <=, >, >= Сравнение зависит от регистра и полагается на алфавитный порядок символовЧтение и запись строк
Как уже упоминалось в главе 1, для чтения и записи значений встроенных типов, таких как int, double и т.д., используется библиотека iostream. Для чтения и записи строк используются те же операторы ввода и вывода.
// Обратите внимание: перед компиляцией этот код следует дополнить
// директивами #include и объявлениями using
int main() {
string s; // пустая строка
cin >> s; // чтение разделяемой пробелами строки в s
cout << s << endl; // запись s в поток вывода
return 0;
}
Программа начинается с определения пустой строки по имени s. Следующая строка читает данные со стандартного устройства ввода и сохраняет их в переменной s. Оператор ввода строк читает и отбрасывает все предваряющие непечатаемые символы (например, пробелы, символы новой строки и табуляции). Затем он читает значащие символы, пока не встретится следующий непечатаемый символ.
Таким образом, если ввести " Hello World! " (обратите внимание на предваряющие и завершающие пробелы), фактически будет получено значение "Hello" без пробелов.
Подобно операторам ввода и вывода встроенных типов, операторы строк возвращают как результат свой левый операнд. Таким образом, операторы чтения или записи можно объединять в цепочки.
string s1, s2;
cin >> s1 >> s2; // сначала прочитать в переменную s1,
// а затем в переменную s2
cout << s1 << s2 << endl; // отобразить обе строки
Если в этой версии программы осуществить предыдущий ввод, " Hello World! ", выводом будет "HelloWorld!".
Чтение неопределенного количества строк
В разделе 1.4.3 уже рассматривалась программа, читающая неопределенное количество значений типа int. Напишем подобную программу, но читающую строки.
int main() {
string word;
while (cin >> word) // читать до конца файла
cout << word << endl; // отобразить каждое слово с новой строки
return 0;
}
Здесь чтение осуществляется в переменную типа string, а не int. Условие оператора while, напротив, выполняется так же, как в предыдущей программе. Условие проверяет поток после завершения чтения. Если поток допустим, т.е. не встретился символ конца файла или недопустимое значение, выполняется тело цикла while. Оно выводит прочитанное значение на стандартное устройство вывода. Как только встречается конец файла (или недопустимый ввод), цикл while завершается.
Применение функции getline() для чтения целой строки
Иногда игнорировать пробелы во вводе не нужно. В таких случаях вместо оператора >> следует использовать функцию getline(). Функция getline() получает поток ввода и строку. Функция читает предоставленный поток до первого символа новой строки и сохраняет прочитанное, исключая символ новой строки, в своем аргументе типа string. Встретив символ новой строки, даже если это первый символ во вводе, функция getline() прекращает чтение и завершает работу. Если символ новой строки во вводе первый, то возвращается пустая строка.
Подобно оператору ввода, функция getline() возвращает свой аргумент типа istream. В результате функцию getline() можно использовать в условии, как и оператор ввода (см. раздел 1.4.3). Например, предыдущую программу, которая выводила по одному слову в строку, можно переписать так, чтобы она вместо этого выводила всю строку:
int main() {
string line;
// читать строки до конца файла
while (getline(cin, line))
cout << line << endl;
return 0;
}
Поскольку переменная line не будет содержать символа новой строки, его придется вывести отдельно. Для этого, как обычно, используется манипулятор endl, который, кроме перевода строки, сбрасывает буфер вывода.
Символ новой строки, прекращающий работу функции getline(), отбрасывается и в строковой переменной не сохраняется.
Строковые операции size() и empty()
Функция empty() (пусто) делает то, что и ожидается: она возвращает логическое значение true (раздел 2.1), если строка пуста, и значение false — в противном случае. Подобно функции-члену isbn() класса Sales_item (см. раздел 1.5.2), функция empty() является членом класса string. Для вызова этой функции используем точечный оператор, позволяющий указать объект, функцию empty() которого необходимо вызвать.
А теперь пересмотрим предыдущую программу так, чтобы она выводила только непустые строки:
// читать ввод построчно и отбрасывать пустые строки
while (getline(cin, line))
if (!line.empty())
cout << line << endl;
Условие использует оператор логического NOT (оператор !). Он возвращает инверсное значение своего операнда типа bool. В данном случае условие истинно, если строка line не пуста.
Функция size() возвращает длину строки (т.е. количество символов в ней). Давайте используем ее для вывода строк длиной только больше 80 символов.
string line;
// читать ввод построчно и отображать строки длиной более 80 символов
while (getline(cin, line))
if (line.size() > 80)
cout << line << endl;
Тип string::size_type
Вполне логично ожидать, что функция size() возвращает значение типа int, а учитывая сказанное в разделе 2.1.1, вероятней всего, типа unsigned. Но вместо этого функция size() возвращает значение типа string::size_type. Этот тип требует более подробных объяснений.
В классе string (и нескольких других библиотечных типах) определены вспомогательные типы данных. Эти вспомогательные типы позволяют использовать библиотечные типы машинно-независимым способом. Тип size_type — это один из таких вспомогательных типов. Чтобы воспользоваться типом size_type, определенным в классе string, применяется оператор области видимости (оператор ::), указывающий на то, что имя size_type определено в классе string.
Хотя точный размер типа string::size_type неизвестен, можно с уверенностью сказать, что этот беззнаковый тип (см. раздел 2.1.1) достаточно большой, чтобы содержать размер любой строки. Любая переменная, используемая для хранения результата операции size() класса string, должна иметь тип string::size_type.
По общему признанию, довольно утомительно вводить каждый раз тип string::size_type. По новому стандарту можно попросить компилятор самостоятельно применить соответствующий тип при помощи спецификаторов auto или decltype (см. раздел 2.5.2):
auto len = line.size(); // len имеет тип string::size_type
Поскольку функция size() возвращает беззнаковый тип, следует напомнить, что выражения, в которых смешаны знаковые и беззнаковые данные, могут дать непредвиденные результаты (см. раздел 2.1.2). Например, если переменная n типа int содержит отрицательное значение, то выражение s.size() < n почти наверняка истинно. Оно возвращает значение true потому, что отрицательное значение переменной n преобразуется в большое беззнаковое значение.
Проблем преобразования между беззнаковыми и знаковыми типами можно избежать, если не использовать переменные типа int в выражениях, где используется функция size().
Сравнение строк
Класс string определяет несколько операторов для сравнения строк. Эти операторы сравнивают строки посимвольно. Результат сравнения зависит от регистра символов, символы в верхнем и нижнем регистре отличаются.
Операторы равенства (== и !=) проверяют, равны или не равны две строки соответственно. Две строки равны, если у них одинаковая длина и одинаковые символы. Операторы сравнения (<, >, <=, >=) проверяют, меньше ли одна строка другой, больше, меньше или равна, больше или равна другой. Эти операторы используют ту же стратегию, старшинство символов в алфавитном порядке в зависимости от регистра.
1. Если длина у двух строк разная и если каждый символ более короткой строки совпадает с соответствующим символом более длинной, то короткая строка меньше длинной.
2. Если символы в соответствующих позициях двух строк отличаются, то результат сравнения определяется первым отличающимся символом.
Для примера рассмотрим следующие строки:
string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
Согласно правилу 1 строка str меньше строки phrase. Согласно правилу 2 строка slang больше, чем строки str и phrase.
Присвоение строк
Как правило, библиотечные типы столь же просты в применении, как и встроенные. Поэтому большинство библиотечных типов поддерживают присвоение. Строки не являются исключением, один объект класса string вполне можно присвоить другому.
string st1(10, 'c'), st2; // st1 - сссссссссс; st2 - пустая строка
st1 = st2; // присвоение: замена содержимого st1 копией st2
// теперь st1 и st2 - пустые строки
Сложение двух строк
Результатом сложения двух строк является новая строка, объединяющая содержимое левого операнда, а затем правого. Таким образом, при применении оператора суммы (оператор +) к строкам результатом будет новая строка, символы которой являются копией символов левого операнда, сопровождаемые символами правого операнда. Составной оператор присвоения (оператор +=) (см. раздел 1.4.1) добавляет правый операнд к строке слева:
string s1 = "hello, ", s2 = "world ";
string s3 = s1 + s2; // s3 - hello, world
s1 += s2; // эквивалентно s1 = s1 + s2
Сложение строк и символьных строковых литералов
Как уже упоминалось в разделе 2.1.2, один тип можно использовать там, где ожидается другой тип, если есть преобразование из данного типа в ожидаемый. Библиотека string позволяет преобразовывать как символьные, так и строковые литералы (см. раздел 2.1.3) в строки. Поскольку эти литералы можно использовать там, где ожидаются строки, предыдущую программу можно переписать следующим образом:
string s1 = "hello", s2 = "world"; // в s1 и s2 нет пунктуации
string s3 = s1 + ", " + s2 + ' ';
Когда объекты класса string смешиваются со строковыми или символьными литералами, то по крайней мере один из операндов каждого оператора + должен иметь тип string.
string s4 = s1 + ", "; // ok: сложение строки и литерала
string s5 = "hello" + ", "; // ошибка: нет строкового операнда
string s6 = s1 + ", " + "world"; // ok: каждый + имеет
// строковый операнд
string s7 = "hello" + ", " + s2; // ошибка: нельзя сложить строковые
// литералы
В инициализации переменных s4 и s5 задействовано только по одному оператору, поэтому достаточно просто проверить его корректность. Инициализация переменной s6 может показаться странной, но работает она аналогично объединенным в цепочку операторам ввода или вывода (см. раздел 1.2). Это эквивалентно следующему коду:
string s6 = (s1 + ", ") + "world";
Часть s1 + ", " выражения возвращает объект класса string, она составляет левый операнд второго оператора +. Это эквивалентно следующему коду:
string tmp = s1 + ", "; // ok: + имеет строковый операнд
s6 = tmp + "world"; // ok: + имеет строковый операнд
С другой стороны, инициализация переменной s7 недопустима, и это становится очевидным, если заключить часть выражения в скобки:
string s7 = ("hello" + ", ") + s2; // ошибка: нельзя сложить строковые
// литералы
Теперь довольно просто заметить, что первая часть выражения суммирует два строковых литерала. Поскольку это невозможно, оператор недопустим.
По историческим причинам и для совместимости с языком С строковые литералы не принадлежат к типу string стандартной библиотеки. При использовании строковых литералов и библиотечного типа string, не следует забывать, что это разные типы.
Упражнения раздела 3.2.2
Упражнение 3.2. Напишите программу, читающую со стандартного устройства ввода по одной строке за раз. Измените программу так, чтобы читать по одному слову за раз.
Упражнение 3.3. Объясните, как символы пробелов обрабатываются в операторе ввода класса string и в функции getline().
Упражнение 3.4. Напишите программу, читающую две строки и сообщающую, равны ли они. В противном случае программа сообщает, которая из них больше. Затем измените программу так, чтобы она сообщала, одинаковая ли у строк длина, а в противном случае — которая из них длиннее.
Упражнение 3.5. Напишите программу, читающую строки со стандартного устройства ввода и суммирующую их в одну большую строку. Отобразите полученную строку. Затем измените программу так, чтобы отделять соседние введенные строки пробелами.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК