Как подключить строки в с

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.

ПРИМЕЧАНИЕ

У такого подхода есть как положительные, так и отрицательные стороны. Но положительных больше. К первым относятся.
1. Натуральное представление строки в том смысле, что начало строки совпадает с началом последовательности символов
2. Неограниченная длина строки — в случае хранения длины строки отдельно длина строки не может превышать значения, максимального для переменной этого типа. Например, при использовании однобайтовых строк и хранении длины строки в первом элементе последовательности (как в Pascal) длина строки не может превышать максимального значения переменной типа char — на многих платформах это 255.
3. «Сдвинув» начало строки вправо, мы все равно получаем абсолютно нормальную строку, заканчивающуюся нулем. Строка, размер которой хранится отдельно, этой особенности лишена.
Недостатки:
1. Для определения длины строки всякий раз необходимо искать ее конец, сравнивая ее символы с нулевым. Чем длиннее строка — тем больше операций. Об этом следует помнить при использовании длины строки, например, в циклах. Если между итерациями строка не изменяется, ее длину следует вычислить заранее, а потом использовать полученное значение.
2. Строки, с которыми используются стандартные функции, не могут содержать в себе нулевые символы. В редких случаях это может доставить неудобство.

Теперь с высоты наших знаний вернемся к строковым литералам. Я думаю, никого не удивит, что их тип – const char*, т.е. указатель на константные символы, а в конце каждого строкового литерала компилятор автоматически подставляет нуль-терминатор.

Символы

Символы сами по себе тоже довольно интересны. Как вы уже знаете, символьная переменная — это переменная типа char, занимающая в памяти 1 байт. В отличие от строк, символ — это встроенный интегральный тип в С/C++, для него допустимы все операции, допустимые для интегральных типов. Существуют символьные литералы, они записываются в одинарных кавычках (прямых апострофах). Пример символьного литерала:

В вышеприведенном примере значением sym является 65 в кодовой таблице ASCII. В этом случае строка sym=’A’ абсолютно эквивалентна строке sym=65. Однако, в целях улучшения портируемости лучше всегда использовать запись в апострофах — рано или поздно программу может потребоваться скомпилировать на платформе, где у символа А другой код.

Для записи символьных литералов типа wchar_t используется запись, аналогичная записи для строковых литералов этого типа:

Существует специальный формат для записи символьных литералов – слеш, за которым идет код символа. Такая форма записи необходима, если мы хотим использовать элемент, не отображающийся в печатный символ, например нуль-терминатор, который представляется так: ’\0’.

ПРИМЕЧАНИЕ

строки (1), (2) и (3) имеют один и тот же эффект. Однако вторая и третья запись считаются нагляднее – мы сразу видим, что работаем именно с символом.

ПРЕДУПРЕЖДЕНИЕ

Не путайте ‘\0’, ‘0’ и “0”. Первое – символьный литерал, соответствующий символу с кодом 0. Второе – такой же литерал, но обозначающий цифру 0, ее код в ASCII-кодировке 48. Третий — это строковый литерал, содержащий два символа, цифру 0 и нуль-терминатор.

Как я уже упоминал тип char – интегральный, а значит для него определены все операции, определенные для интегральных типов, в том числе +, -, *, / и операции сравнения. Хотя не все они имеют смысл для символов (например, я плохо себе представляю смысл перемножения двух символов), однако их использование совершенно «законно» и их результат обычно именно такой, какой ожидался.

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

Операции со строками

Создание строк

Проиллюстрирую создание строк на фрагментах кода с комментариями.

Присваивание строк

Первый и самый очевидный способ присваивания строк – присваивание отдельных символов. Например,

Однако, это совершенно неудобно. Не зная о правильных способах, начинающие программисты часто «выдумывают» свои способы присваивания строк, конечно, неправильные. Приведу несколько примеров:

Эта ошибка относительно безопасна, так как приводит к сбою на этапе компиляции. Есть и гораздо более опасная ошибка.

Этот код откомпилируется, но, возможно, содержит «идеологическую» ошибку. Неправильно полагать, что в str2 теперь содержится копия str1. На самом деле этот указатель указывает не на копию, а на ту же самую строку. При любом изменении содержимого str2 изменяется str1. Однако, если именно это и требуется, то все в порядке.

Еще один вариант присваивания указателей – присваивание их строковым литералам. Как вы помните, тип строкового литерала – const char*, а значит такой код работает:

Опять же следует помнить, что str указывает на строковый литерал, а не на его копию. Но, к сожалению, такой код тоже сработает:

Здесь мы имеем дело с наследством C, в котором отсутствовал const. Поэтому стандарт С++ разрешает такое присваивание. Что может иметь неприятные последствия:

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

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

Для копирования строк существуют несколько библиотечных функций, наиболее общеупотребительной из которых является функция

Функция посимвольно копирует содержимое строки, на которую указывает src в строку, на которую указывает dest и возвращает dest. Так как массив может быть преобразован в указатель, такой вызов функции абсолютно легален:

При использовании этой функции следует соблюдать осторожность. Опасность заключается в том, что даже если исходная строка окажется больше, чем память, выделенная для второй строки (программистом через malloc или компилятором при использовании массивов), функция strcpy никак про это узнать не сможет и продолжит копирование в невыделенную память. Разумеется, последствия будут катастрофическими.

Снизить риск такого развития событий способна функция

Последний параметр – максимальное количество копируемых символов. Таким образом, передавая туда размер приемника, вы гарантируете, что функция никогда не выйдет за пределы выделенной памяти. Однако помните, что если исходная строка будет скопирована не полностью, нуль-терминатор не появится в результирующей строке. Его придется записать самостоятельно.

ПРЕДУПРЕЖДЕНИЕ

Никогда не забывайте контролировать используемую память!

Сравнение строк

Для лексикографического сравнения строк используются функции strcmp и stricmp. Первая сравнивает строки с учетом регистра, вторая – без. Однако, все это относится только к латинице. Если вы хотите сравнивать без учета регистра кириллические строки, придется разобраться с локалями.

Прототипы этих функций таковы:

Обе функции возвращают число меньшее 0, если первая строка меньше второй, большее нуля если первая строка больше второй и 0, если строки лексикографически равны.

Полагаю, вам не придет в голову сравнивать строки, используя операции ‘<’ и ‘>’.

Длина строки

Для вычисления длины строки используется функция

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

больше подойдет примерно такой код :

Преобразования строк

Зачастую требуется преобразовать число в строку и наоборот. Есть несколько способов сделать это.

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

Во-вторых, можно использовать функции sprintf и sscanf. Например, так:

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

Хотя sprintf и sscanf довольно удобны, у них есть несколько недостатков. Во-первых, они не всегда быстро работают, во-вторых не типобезопасны. Например, если в строке формата вы укажите, что передаете два целых, а вместо этого передадите два double, ошибка обнаружится только при выполнении программы и найти ее причину будет не так-то просто.

В-третьих, доступно целое семейство функций atof, atoi, atol и itoa, ltoa. Все они очень похоже между собой. Функции из первой группы преобразуют строку в число (float, int или long) в зависимости от окончания. Функции из второй группы выполняют обратное преобразование. Должен заметить, что функции из второй группы (равно как и ранее упомянутая stricmp) не входят в стандарт С, однако они весьма удобны и доступны на некоторых платформах.

Прототипы функций из первой группы:

Функции из второй группы могут создавать строковое представление чисел в любой системе (по любому основанию) от 2 до 36. Основание передается в третьем параметре. Чтобы получить строковое представление числа в десятичной системе, передайте 10. Функции возвращают указатель на строку.

ПРИМЕЧАНИЕ

При использовании этих функций не забывайте выделять память, достаточную для предоставления строкового предоставления числа. Например, максимальная длина десятичного строкового представления четырехбайтного беззнакового целого числа — 11 байт, включая нуль-терминатор («4294967295»).

Конкатенация (объединение) строк

Сначала простой вопрос – каков результат выполнения следующего кода:

Если ответ – ошибка на этапе компиляции, материал изложенный в статье вы усвоили (или знали это раньше). Если же вы полагаете, что в str3 будет хранится строка «Hello world!», то вероятно, мои предыдущих объяснений оказалось недостаточно. Нельзя складывать указатели (и имена массивов).

Для конкатенации следует использовать функции.

Есть две специальные функции:

Эти функции добавляют к строке, на которую указывает dest, символы из строки source. Первая версия добавляет все символы до нуль-терминатора, вторая – максимум size символов. Результирующая строка завершается нуль-терминатором.

Кроме того, можно воспользоваться общей функцией sprintf так:

Этот вариант удобнее, если нужно объединить более двух строк. Однако к его недостаткам относится типонебезопасность.

Заключение

На этом первая часть, посвященная С-строкам заканчивается. В качестве домашнего задания рекомендую реализовать упоминавшиеся здесь стандартные функции, за исключением sprintf и sscanf самостоятельно. Это нетрудно, и если вы справитесь, значит, вы отлично овладели материалом. Тем не менее, всегда используйте стандартные функции, а не ваши собственные. Это общее правило – реализовать стандартные функции имеет смысл только в качестве учебного задания.

Для использования строковых функций вам потребуется подключить к программе соответствующие стандартные заголовки. Это string.h для всех функций, кроме sprintf и sscanf, определенных в stdio.h и функций преобразования, определенных в stdlib.h.

В С++ вместо вышеупомянутых заголовочных файлов следует подключать cstring, cstdio и cstdlib соответственно.

Во второй части статьи я рассмотрю возможности C++, позволяющее значительно упростить работу со строками и сделать ее более удобной.

Как подключить строки в с

Стандартная библиотека языка С++ включает класс string , предназначенного для работы с массивом символов. Этот класс более безопасен в использовании, чем С-строка, функции для работы с которой также доступны.
В строках типа string завершающий символ не добавляется в массив, но может содержаться в массиве, как и любой другой символ. Инструментарий класса string более чем достаточен, чтобы не прибегать к работе со строкой в стиле Си. Вместе с тем, функции класса могут работать не только с экземплярами класса, но и с Cи-строкой. C-строки автоматически преобразуются в строки типа string .
Примечание. Хотя обратное преобразование и не производится, но существует возможность копировать символы в символьные массивы (с завершающим символом) используя функцию c_str() (или data() ).
В отличие от статического символьного C-массива, контейнер string представляет строку как динамический массив. Для работы с контейнером string стандартная библиотека предоставляет полный набор функций, общий для всех контейнеров, в частности, функции получения итераторов. Вся работа по распределению памяти выполняется классом string , а не разработчиком. Для начала работы с классом string необходимо подключить заголовочный файл директивой:

Инициализация объекта класса string

Объявление переменных типа string (на языке ООП — создание экземпляра класса) не отличается от объявления переменных других типов:

Примечание. Создается пустая строка myStr .
Инициализировать объекты класса string можно различными способами.

Ниже демонстрируется использование некоторых способов инициализации.
Программа 3

Примечание. Отдельным символом строку инициализировать нельзя!

Ввод и вывод объектов класса string. Функция getline

Для ввода/вывода строки используются глобальные потоковые объекты cin и cout и операции >> и << , соответственно. Если установлен флаг skipws начальные пробелы будут проигнорированы. В поток будут поступать символы, пока не будет встречен пробельный символ (к ним относятся следующие: собственно пробел, \t , \v , \r , \n и \f ) или EOF . В программе пробельный символ будет расцениваться как нулевое значение (т. е. конец строки) и в строковую переменную будет записана только часть строки, до первого пробела, остальная часть останется на очереди — в потоке. Такую строку получает переменная myStr в программе 4 (показано на выводе, стр. 11). Произвести сцепку оставшихся слов в потоке можно циклом while. (Смотрите ниже).
Программа 4

Чтобы ввести строку полностью, включая пробельные символы, необходимо использовать функцию getline() .
Примечание. Не путайте эту глобальную функцию класса string с функцией cin.getline() (методом класса istream ), которая обычно применяется для C-строки.
Синтаксис этой функции таков:

Аргументы функции:
is — потоковый объект ввода;
str — объект класса string , куда будет помещена строка;
char_delim — третий аргумент (необязательный) используется для указания завершающего символа. Если третьего аргумента нет, то предполагается, что завершающий символ — ‘\n’ .
Функция считывает строку полностью с завершающим символом. Сам символ новой строки отбрасывается и в строковой переменной не сохраняется. Также не добавляется нулевой символ. Символ новой строки завершает выполнение функции, где бы он не находился.
Если нулевой символ находится в начале строки, то функция вернет пустую строку. Рассмотрим этот случай:
Программа 5

После выполнения инструкции в строке 14: getline(cin, S2); и вывода C-строки программа выводит пустую строку и завершает свою работу. Причина того, что getline не приняла новую строку в том, что поток ввода оказался не пуст! После ввода С-строки ( ch ) в потоке остается завершающий символ (после выполнения операции ввода), этот символ и перехватила функция getline . Для очистки потока можно применить функцию cin.ignore() (или cin.get() ).

Конкатенация. Метод append()

В операции конкатенации (лат. concatenatio — «сцепле́ние») происходит объединение строк. Результатом сложения двух строк — является новая строка string. В этих операциях могут участвовать наряду с объектами класса string строковые литералы, одиночные символы и C-строки. Если объекту класса string осуществляется попытка присвоить значение конкатенации литералов (в конкатенации не участвуют объекты класса string ), то это приведет к ошибке. Для конкатенации применяются операции «+» и «+ +=» трактуется как «добавление в конец» строки. Например:

В результате выполнения первой инструкции в str3 будет записана вначале строка str1 , а затем строка str2 . (Значение, которое строка str3 имела до операции присваивания, будет утрачено). Результат второй инструкции: к символам строки str1 будут дописаны символы строки str3 . В третьей инструкции строке str2 будет передано значение строки str1 , дополненной литералом.

append()

Фактически операция «+ margin:0px;padding:0px;border:0px;font-family:Monaco,Consolas,Andale Mono,DejaVu Sans Mono,monospace;background-color:transparent»>append() . Однако, данный метод гибче, так как позволяет добавлять элементы различными способами, например:
1. подстроку из строки other_str в промежутке [pos, pos + count) :

2. подстроку определённую итераторами first и last :

3. или более лаконичное добавление count символов ch :

Дана непустая строка S и целое число N (> 0) . Вывести строку, содержащую символы строки S , между которыми вставлено по N символов ‘*’ .
Программа 6

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *