C++. Работа со строками. Класс string. Конструкторы класса. Функции assign(), append(), insert(), replace(), erase(), find(), rfind(), compare(), c_str(). Примеры
Класс string предназначен для работы со строками типа char* , которые представляют собой строку с завершающим нулем. Класс string был введенн как альтернативный вариант для работы со строками типа char* . Строки, которые завершаются символом ‘\0’ еще называются C-строками. Поскольку, string есть классом, то можно объявлять объекты этого класса.
2. Какие модули (библиотеки) нужно подключить, чтобы использовать возможности класса string в MS Visual Studio C++?
Чтобы использовать возможности класса string в MS Visual Studio (C++), нужно подключить библиотеку <string> и пространство имен std .
3. Каким образом осуществляется объявление переменной типа string ? Примеры
Объявление переменной типа string осуществляется точно так же как и обычной переменной. Возможный вариант объявления с одновременной инициализацией.
4. Какие преимущества и недостатки дает использование класса string в сравнении с типом char* ?
Создание нового типа string было обусловлено недостатками работы с строками символов, который демонстрировал тип char* . В сравнении с типом char* тип string имеет следующие основные преимущества:
- возможность обработки строк стандартными операторами C++ ( = , + , = = , <> и т.п.). Как известно, при использовании типа char* даже наиболее простые операции со строками выглядели сложно и требовали написания чрезмерного программного кода;
- обеспечение лучшей надежности (безопасности) программного кода. Например, при копировании строк, тип string обеспечивает соответствующие действия, которые могут возникнуть в случае, если строка-источник имеет больший размер чем строка-приемник;
- обеспечение строки, как самостоятельного типа данных. Объявление типа string как строки есть единым для всех переменных в программе, которая обеспечивает непротиворечивость данных.
Основным недостатком типа string в сравнении с типом char* , есть замедленная скорость обработки данных. Это связано с тем, что тип string – это, фактически, контейнерный класс. А работа с классом требует дополнительной реализации программного кода, который, в свою очередь занимает лишнее время.
5. Какие операторы можно использовать с объектами класса string ?
Класс string есть удобен тем, что позволяет удобно манипулировать строками, используя стандартные (перегруженные) операторы.
С объектами класса string можно использовать нижеследующие операторы
- = – присваивание
- + – конкатенация (объединение строк)
- += – присваивание с конкатенацией
- == – равенство
- != – неравенство
- < – меньше
- <= – меньше или равно
- > – больше
- >= – больше или равно
- [ ] – индексация
Пример, который демонстрирует использование вышеприведенных операторов
6. Содержит ли класс string конструкторы?
Как и любой класс, класс string имеет ряд конструкторов. Основные из них следующие:
7. Примеры инициализации с помощью конструкторов
Ниже приведены примеры инициализации переменных типа string
8. Присваивание строк. Функция assign() . Примеры
Чтобы присвоить одну строку другой, можно применить один из двух методов:
- использовать оператор присваивания ‘=’ ;
- использовать функцию assign() из класса string .
Функция assign() имеет несколько перегруженных реализаций.
Первый вариант – это вызов функции без параметров
В этом случае происходит простое присваивание одной строки другой.
Второй вариант позволяет копировать заданное количество символов из строки:
- s – объект, из которого берется исходная строка;
- st – индекс (позиция) в строке, из которой начинается копирование num символов;
- num – количество символов, которые нужно скопировать из позиции st ;
- size_type – порядковый тип данных.
Третий вариант функции assign() копирует в вызывающий объект первые num символов строки s :
- s – строка, которая завершается символом ‘\0’ ;
- num – количество символов, которые копируются в вызывающий объект. Копируются первые num символов из строки s .
Ниже приведен пример с разными реализациями функции assign() .
Пример.
9. Объединение строк. Функция append() . Пример
Для объединения строк используется функция append() . Для добавления строк также можно использовать операцию ‘+’ , например:
Однако, функция append() хорошо подходит, если нужно добавлять часть строки.
Функция имеет следующие варианты реализации:
В первом варианте реализации функция получает ссылку на строчный объект s , который добавляется к вызывающему объекту. Во втором варианте реализации функция получает указатель на строку типа const char * , которая завершается символом ‘\0’ .
Пример. Демонстрация работы функции append() .
10. Вставка символов в строке. Функция insert() . Пример
Чтобы вставить одну строку в заданную позицию другой строки нужно использовать функцию insert() , которая имеет несколько вариантов реализации.
Первый вариант функции позволяет вставить полностью всю строку s в заданную позицию start вызывающей строки (вызывающего объекта):
Второй вариант функции позволяет вставить часть (параметры insStart , num ) строки s в заданную позицию start вызывающей строки:
В вышеприведенных функциях:
- s – строка, которая вставляется в вызывающую строку;
- start – позиция в вызывающей строке, из которой осуществляется вставка строки s ;
- insStart – позиция в строке s , из которой происходит вставка;
- num – количество символов в строке s , которые вставляются с позиции insStart .
11. Замена символов в строке. Функция replace() . Пример
Функция replace() выполняет замену символов в вызывающей строке. Функция имеет следующие варианты реализации:
В первом варианте реализации вызывающая строка заменяется строкой s . Есть возможность задать позицию ( start ) и количество символов ( num ) в вызывающей строке, которые нужно заменить строкой s .
Второй вариант функции replace() отличается от первого тем, что позволяет заменять вызывающую строку только частью строки s . В этом случае задаются два дополнительных параметра: позиция replStart и количество символов в строке s , которые образуют подстроку, которая заменяет вызывающую строку.
Пример. Демонстрация работы функции replace() .
12. Удаление заданного количества символов из строки. Функция erase() . Пример
Для удаления символов из вызывающей строки используется функция erase() :
- index – индекс (позиция), начиная из которой нужно удалить символы в вызывающей строке;
- num – количество символов, которые удаляются.
Пример.
13. Поиск символа в строке. Функции find() и rfind() . Примеры
В классе string поиск строки в подстроке можно делать двумя способами, которые отличаются направлением поиска:
- путем просмотра строки от начала до конца с помощью функции find() ;
- путем просмотра строки от конца к началу функцией rfind() .
Прототип функции find() имеет вид:
- s – подстрока, которая ищется в строке, что вызывает данную функцию. Функция осуществляет поиск первого вхождения строки s . Если подстрока s найдена в строке, что вызвала данную функцию, тогда возвращается позиция первого вхождения. В противном случае возвращается -1;
- start – позиция, из которой осуществляется поиск.
Прототип функции rfind() имеет вид:
- s – подстрока, которая ищется в вызывающей строке. Поиск подстроки в строке осуществляется от конца к началу. Если подстрока s найдена в вызывающей строке, то функция возвращает позицию первого вхождения. В противном случае функция возвращает -1;
- npos – позиция последнего символа вызывающей строки;
- start – позиция, из которой осуществляется поиск.
Пример 1. Фрагмент кода, который демонстрирует результат работы функции find()
Пример 2. Демонстрация работы функции rfind() .
14. Сравнение частей строк. Функция compare() . Пример
Поскольку тип string есть классом, то, чтобы сравнить две строки между собой можно использовать операцию ‘= =’ . Если две строки одинаковы, то результат сравнения будет true . В противном случае, результат сравнения будет false .
Но если нужно сравнить часть одной строки с другой, то для этого предусмотрена функция compare() .
Прототип функции compare() :
- s – строка, которая сравнивается с вызывающей строкой;
- start – позиция (индекс) в строке s , из которой начинается просмотр символов строки для сравнения;
- num – количество символов в строке s , которые сравниваются с вызывающей строкой.
Функция работает следующим образом. Если вызывающая строка меньше строки s , то функция возвращает -1 (отрицательное значение). Если вызывающая строка больше строки s , функция возвращает 1 (положительное значение). Если две строки равны, функция возвращает 0.
Пример. Демонстрация работы функции compare() :
15. Получение строки с символом конца строки ‘\0’ ( char * ). Функция c_str() . Пример
Чтобы получить строку, которая заканчивается символом ‘\0’ используется функция c_str() .
Функция объявлена с модификатором const . Это означает, что функция не может изменять вызывающий объект (строку).
Пример 1. Преобразование типа string в const char * .
Пример 2.
Ниже продемонстрирован перевод строки из string в тип System::String для отображения его в элементе управления типа Label для приложений типа Windows Forms Application .
16. Как определить длину строки типа string ? Функция length()
Для определения количества символов в строке используется функция length() без параметров.
Строки в C++
В этой статье мы поговорим о классе string, который включен в стандартную библиотеку языка программирования C++. Одновременно с этим рассмотрим основы работы со строками: изучим конструкторы, арифметические операторы и некоторые методы.
В языке программирования C++ существует специальный класс string, обеспечивающий удобство работы со строковыми данными. Для его применения нет необходимости выполнять хитрые манипуляции — достаточно просто подключить соответствующий заголовочный файл string.
Если вспомнить тот же Си, то там существует заголовочный файл стандартной библиотеки, который называется string.h. Он содержит функции, необходимые для работы с памятью и нуль-терминированными строками. На практике функции, объявленные в string.h, применяются довольно широко, так как являются частью стандартной библиотеки, в результате чего стабильно работают на любых платформах, которые поддерживают язык программирования Си.
Но вернемся к С++. На практике вы можете не просто объявлять строки, но и одновременно с этим присваивать им значения:
В нашем случае S1 будет являться пустой, а S2 — состоять из пяти символов.
Идем дальше. К отдельным символам вы можете обращаться по индексу, как к элементам массива. К примеру, S[0] позволит обратиться к первому строчному элементу (как и в массиве, нумерация начинается с нуля).
Если же надо узнать длину строки length, вы можете воспользоваться методом size(). К примеру, последний символ строки S можно выразить как S[S.size() — 1] .
Конструкторы
При работе со строковыми данными возможно применение ряда конструкторов:
Вызов конструкторов может быть явным, как в примере ниже:
То есть мы осуществили вызов конструктора string в целях создания строчки, которая включает в себя 10 символов ‘z’.
Вызов бывает и неявным, что происходит в процессе объявления, когда указываются дополнительные параметры. Это может выглядеть следующим образом:
Ввод-вывод
Вывод строчных значений осуществляется так же, как и числовых:
Если же надо выполнить считывание, используют операцию «>>» для объекта cin:
Результатом вышеуказанной операции станет считывание строчки из непробельных символов, то есть пробелы и концы строк будут пропущены. Это довольно удобно, если надо разбить текст на слова либо в целях считывания данных до конца файла посредством while (cin >> S) .
Есть возможность выполнять считывание и до появления символа конца строки — тут уже пригодится функция getline. А вот сам символ конца считывается уже из входного потока, правда, непосредственно к строке его добавление не происходит:
Арифметические операторы
Работая со строковыми данными, мы можем выполнять ряд арифметических операций, среди них:
- = — для присваивания значения;
- += — для добавления в конец какого-нибудь другого элемента (к примеру, числа) либо даже другой строки;
- + — оператор конкатенации двух строк/строки и символа;
- ==, != — для посимвольного сравнения;
- <, >, <=, >= — для лексикографического сравнения.
Таким образом, мы можем выполнить копирование содержимого одной строки в другую посредством операции S1 = S2 , далее выполнить их сравнение на равенство посредством S1 == S2 , потом сравнить в лексикографическом порядке ( S1 < S2 ) либо или выполнить конкатенацию (сложение) в виде S = S1 + S2 .
Методы
Для работы со строковыми данными существуют разнообразные методы, причем многие из них вы сможете использовать разнообразными способами (с различным набором параметров). Давайте рассмотрим некоторые из существующих методов.
size
Возвращает длину строки. Значение, которое таким образом возвращается, имеет беззнаковый тип (как и во всех других случаях, если функция возвращает значение, которое равняется длине строке либо индексу элемента, такие значения являются беззнаковыми). Именно поэтому следует предельно внимательно выполнять вычитание из значения, которое возвращается посредством size(). К примеру, будет ошибкой записать цикл, перебирающий все строчные элементы, кроме последнего, типа for (int i = 0; i < S.size() — 1; ++i) .
Также длину строки можно возвратить с помощью метода length().
resize
S.resize(n) позволит изменить длину строки, причем новая длина станет равняться n. Важно понимать, что длина строки ведь может как увеличиться, так и уменьшится. Вызвав метод в виде S.resize(n, c), когда c — символ, вы получите ситуацию, при которой при увеличении строковой длины добавляемые элементы станут равняться c.
clear
S.clear() очистит строчку, в результате чего она станет пустой.
empty
S.empty() возвратит true, когда строчка будет пустой и false, когда непустой.
push_back
S.push_back(c) добавит в конец символ c. Этот метод вызывается с одним параметром, имеющим тип данных char.
append
Добавит в конец несколько символов, другую строчку либо ее фрагмент. Тут существует множество способов вызова:
- S.append(n, c)— добавляется n одинаковых элементов, которые равны с. У n здесь целочисленный тип, а у c — char;
- S.append(T) — добавляется содержимое T-строки. Причем T может быть как С-строчкой, так и объектом класса string;
- S.append(T, pos, count) — добавляются символы T-строки, начиная с символа, имеющего индекс pos. Добавление происходит в количестве count.
Хотите знать про C++ больше? Получить нужные навыки вы сможете на курсах в Otus.
Строки в С и С++
Исторически работа со строками в языках С и С++ вызывает затруднения у новичков. Как мне кажется, во многом это связано с тем, что в этих языках строки значительно отличается от строк в языках типа Basic и Pascal. Непонимание базовых принципов приводит к тому, что код либо не работает вовсе, либо что еще хуже, работает, но не стабильно или непредсказуемо даже для его авторов. Основой для написания данной статьи по большей части послужили вопросы, часто задаваемые новичками. Надеюсь, хотя бы часть таких вопросов она снимет.
Это первая часть, в которой обсуждаются «традиционные» строки в С. В С++ существуют более удобные механизмы для работы со строковыми данными, эти механизмы рассматриваются во второй части статьи. А зачем вообще обсуждать неудобные С-строки, если есть С++? К сожалению, совсем забыть о строках в стиле С нельзя по двум причинам:
- существует большое количество библиотек (например, API операционных систем) работающих именно с С-строками
- строковые классы в С++ все равно основаны на традиционных С-строках, и если мы хотим разобраться в том, как они работают, нам придется понимать их основы.
Строковые литералы
Самая простая строковая сущность (под строковой сущностью я понимаю нечто, с чем можно работать как с привычной строкой) в С — это так называемый «строковый литерал». Он представляет собой последовательность символов, заключенную в двойные кавычки. Пример:
Пока я намеренно игнорирую вопрос о том, что есть строковый литерал с точки зрения компилятора или языка, ограничиваясь только его свойствами с точки зрения программиста.
Основное свойство строкового литерала — простота его использования. Не имея ни малейшего представления о том, чем он является на самом деле, мы можем использовать его практически везде, где от нас ждут строку. Например, в WinAPI-функцию SetWindowText (она задает текст, связанный с окном) нужно передать описатель окна и строку текста. И мы можем вызвать ее очень просто:
Но конечно, строковых литералов, естественно, фиксируемых при создании программы, будет маловато. Хотелось бы иметь возможность использовать переменные, не правда ли?
Строковые переменные
В С отсутствуют встроенные строковые типы в том смысле, в котором они есть в языках типа Basic и Pascal. И присущая этим языкам легкость оперирования строковыми переменными (присвоение, сравнение) в С недоступна. Что же такое строка в С?
Для начала разберемся, что такое строка в жизни. Очевидно, что строка – это последовательность символов . В С – как в жизни. С-строка — это последовательность символов. Как известно, последовательности в С представляются массивами или указателями. Между первыми и вторыми существует связь, однако природа этой связи выходит за рамки данной статьи. Предполагается, что читателю знакомы следующие особенности связи между массивами и указателями:
- массив можно привести к указателю на его первый элемент, что неявно происходит при передаче массивов в функции, ожидающие указатели
- информация о размере массива переданного таким образом в функцию теряется
- в С не существует способа передать массив по значению с сохранением его размера
- указателя на первый элемент массива достаточно для работы со всем массивом, при условии что нам известна его длина.
Если вышеприведенные высказывания вызывают у вас затруднения, рекомендую сначала разобраться с этим вопросом, а уже потом читать эту статью.
В дальнейшем для простоты я по большей части буду говорить о массивах, но почти все сказанное относится и к указателям.
Итак, тип строк в С – массив. Однако каков тип элементов этого массива? Вообще говоря, возможны варианты. Исторически символ занимает 1 байт, в этом случае он имеет тип char . При этом существуют и другие кодировки, в которых символ представляется, например, двумя байтами. Для работы с такими строками требуются специальные функции.
Слегка отвлечемся от строк и разберем понятие кодировки . По определению, кодировка — это способ представления чего либо, в нашем случае символов. Кодировки символов делятся на однобайтные — каждый символ представлен одним байтом и многобайтные, в которых одному символу соответствует несколько байтов. В свою очередь многобайтные кодировки можно разделить на кодировки с фиксированным количеством байтов — каждому символу соответствует одинаковое количество байтов, и «плавающие», в которых один символ может представляться разным количеством байтов в зависимости от его содержимого. К первым относятся кодировки типа Unicode, в которой каждый символ представлен двумя байтами, ко вторым — UTF-8 и др. Плавающие кодировки — отдельная тема, языки С/С++ не предлагают для них никакой поддержки.
Необходимость в многобайтных кодировках возникла из-за того, что одним стандартным байтом можно представить не так много символов, например восьмибитный байт способен принимать значения от 0 до 255, а значит в такой кодировке не может существовать более 256 различных символов. Учитывая, что, например, в японском языке около двух тысяч иероглифов, 256 символов японцам явно не хватит. Но уже двух восьмибитных байтов хватит для представления 65536 символов — вполне неплохо, хотя и недостаточно для представления всех символов на свете.
Между однобайтными и фиксированными многобайтными строками принципиальной разницы нет. В С/С++ существует специальный тип для многобайтных символов — wchar_t и специальные функции для работы со строками, состоящими из таких символов. Размер wchar_t не фиксирован в стандарте и определяется реализацией компилятора. На многих платформах и компиляторах это два байта, соответствующих кодировке Unicode. Каждой функции, работающей со строками из однобайтных символов, соответствует функция — «побратим», принимающая строки из многобайтных символов. Кроме того, существует специальная форма для записи строковых литералов, в которых символы представлены несколькими байтами: перед кавычками ставится буква L. Т.е., возвращаясь к нашему первому примеру, вызов функции SetWindowText в Unicode-программе будет выглядеть так:
Фактически, на этом с точки зрения программиста разница между строками из многобайтовых и однобайтовых символов заканчивается, поэтому в дальнейшем я буду рассматривать только традиционные строки из однобайтовых символов. При необходимости вы легко найдете многобайтовые аналоги всех упомянутых в статье функций.
ПРЕДУПРЕЖДЕНИЕ Не забывайте пользоваться правильными функциями! Очевидно, что последовательность должна быть как-то ограничена, то есть мы должны знать, где она заканчивается. Размер строки можно было бы счесть равным размеру массива… но тогда возникают две проблемы:
А значит, нужно поступить как-то по другому. Собственно, у нас есть два варианта – связать со строкой ее размер и где-то хранить его (например, в нулевом элементе массива или специальной переменной), либо ограничить строку (последовательность) специальным символом (элементом последовательности), дойдя до которого мы будем знать, что достигли конца. Именно этот способ и принят в С, причем в качестве терминатора (ограничивающего символа) выбран символ с кодом 0, как наиболее редко встречающийся в строках. То есть в С строка – это массив элементов типа char, ограниченный символом с кодом 0, называемом нуль-терминатором. Напомню, что c массивом элементов типа char связан указатель на char, поэтому С-строка– это еще и указатель типа char* на область памяти, заканчивающуюся символом с кодом 0.
|