Перейти к основному содержимому

2. Строковый класс

Общие сведения

В языке С++ встроенный строковый тип отсутствует, но для более удобной работы со строками был разработан строковый класс. Для работы со строковым классом необходимо подключить к программе заголовочный файл <string>. Строковый класс является классом-контейнером, поэтому поддерживает все алгоритмы библиотеки стандартных шаблонов.

предупреждение

Примечание: std::basic_string не рассматривается стандартом как контейнер, но ведет себя очень похоже из-за своего сходства. Для удобства он указан в справочниках как "псевдоконтейнер".

Подробности смотрите здесь.

Формат оператора описания строки:

string имя;

Пример:

string str1, str2;

Ввод-вывод строк. Для ввода-вывода строк используется как уже известные объекты cin и cout, так и библиотечные функции С++.

Ввод-вывод строки c помощью стандартных потоков ввода-вывода cin и cout:

#include <iostream>
#include <string>

int main() {
std::string str;
std::cin >> str;
std::cout << str << std::endl;
}

Строка вводится точно так же, как и переменные известных нам типов. Если запустите программу и введете строку из нескольких слов, выводится только первое слово. Это связано с тем, что ввод выполняется до первого пробельного символа (то есть пробела, знака табуляции или символа перевода строки \n).

Если требуется ввести строку, состоящую из нескольких слов, в одну строковую переменную, используется функция getline, первым параметром которой является поток cin, а второй параметр – имя переменной, которая получит строку, что введет пользователь с клавиатуры:

#include <iostream>
#include <string>

int main() {
std::string str;
getline(std::cin, str);
std::cout << str << std::endl;
}

Вывод строки происходит как вывод единого целого:

std::cout << "строка = " << str1; 

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

Со строками можно выполнять следующие арифметические операции:

  • = - присваивание значения;
  • += - добавление в конец строки другой строки или символа;
  • + - конкатенация двух строк, конкатенация строки и символа;
  • ==, != - посимвольное сравнение;
  • <, >, <=, >= - лексикографическое сравнение.

То есть можно скопировать содержимое одной строки в другую при помощи операции S1 = S2, сравнить две строки на равенство при помощи S1 == S2, сравнить строки в лексикографическом порядке при помощи S1 < S2, или сделать сложение (конкатенацию) двух строк в виде S = S1 + S2.

Присвоение строк происходит с помощью операции присвоения:

str1 = "Mary";
str2 = str1;

Доступ к отдельным символам строки может происходить также как к элементам массива:

str1[2] = 'n'; // str1="Many";

Операция сцепления строк дописывает в строку еще одного слова или любую строку;

str2 = str1 + "Hello";

Получение длины строки

Методы size() и length() возвращают количество символов в строке. Рассмотрим пример:

#include <iostream>
#include <string>

int main() {
std::string str = "Миру - мир";
std::cout << str.length() << std::endl;
}
```cpp

Если кодировка файла с кодом будет в UTF-8, то результат работы программы:

```bash
17

UTF-8 (от англ. Unicode Transformation Format, 8-bit — "формат преобразования Юникода, 8-бит") — распространённый стандарт кодирования символов, позволяющий более компактно хранить и передавать символы Юникода, используя переменное количество байт (от 1 до 4), и обеспечивающий полную обратную совместимость с 7-битной кодировкой ASCII. Подробнее о реализации UTF-8 в С++ читайте здесь.

Вспомним о том, что тип char в С++ занимает 1 байт памяти. Для хранения символов UTF-8 от не подходит, поэтому в С++ существует типы: wchar_t, char16_t, char32_t, char8_t, о которых подробнее можете узнать здесь.

Проанализируем символы, входящие в строку Миру - мир.

М, и, р, у, м, и, р - 7 символов. Каждый символ занимает по 2 байта, так как они из UTF-8:

| Символ кодовой точки | Символ | Шестнадцатеричное представление | Имя | | --- | --- | --- | | U+041C | М | d0 9c | CYRILLIC CAPITAL LETTER EM | | U+0438 | и | d0 b8 | CYRILLIC SMALL LETTER I | | U+0440 | р | d1 80 | CYRILLIC SMALL LETTER ER | | U+0443 | у | d1 83 | CYRILLIC SMALL LETTER U | | U+043C | м | d0 bc | CYRILLIC SMALL LETTER EM |

\s, -, \s, - 3 символа. Эти символы из кодовой таблицы ASCII и занимают 1 байт памяти.

В итоге получаем, что рассматриваемая строка занимает 72+3=14+3=177 * 2 + 3 = 14 + 3 = 17 байтов памяти. Это ровно столько, сколько вернул метод length().

Следующий код вернет более подходящий вариант, с учетом символов UTF-8:

#include <iostream>
#include <string>

int main() {
std::wstring str{L"Миру - мир"};
std::cout << str.length() << std::endl;
}

В результате получим:

10

Примеры работы со строками

Пример 1

Даны два слова (две переменные). Сколько раз во втором слове встречается первая буква первого слова (не использовать find, erase, substr и т.д.).

#include <iostream>
#include <string>

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string s1, s2;
int result = 0;
std::cout << "Введите первое слово: ";
getline(std::cin, s1);
std::cout << "Введите второе слово: ";
getline(std::cin, s2);
for (int i = 0; i < s2.length(); i++)
if (s1[0] == s2[i]) result++;
std::cout << "\nРезультат:" << std::endl;
std::cout << result << std::endl;
}

Результат выполнения программы:

Введите первое слово: apple
Введите второе слово: apapapap

Результат:
4

Пример 2

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

#include <iostream>
#include <string>

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string s1, s2, s3, result;
std::cout << "Введите первое слово: ";
getline(std::cin, s1);
std::cout << "Введите второе слово: ";
getline(std::cin, s2);
std::cout << "Введите третье слово: ";
getline(std::cin, s3);
result = s1[0];
result += " ";
result += s2[0];
result += " ";
result += s3[0];
std::cout << "\nРезультат:" << std::endl;
std::cout << result << std::endl;
}

Результат выполнения программы:

Введите первое слово: Take
Введите второе слово: My
Введите третье слово: Money

Результат:
T M M

Пример 3

Имеется некоторая последовательность символов. Образовать новую последовательность, включив в нее символы исходной, кроме символов g и v (не использовать склейку +, find, erase, substr и т.д.).

#include <iostream>
#include <string>

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string text, result = "";
std::cout << "Введите текст: ";
getline(std::cin, text);
for (int i = 0; i < text.length(); i++)
if (text[i] != 'g' && text[i] != 'v') result += text[i];
std::cout << "\nРезультат:" << std::endl;
std::cout << result << std::endl;
}

Результат выполнения программы:

Введите текст: Python is a programming language that lets you work quickly and integrate systems more effectivaly.

Результат:
Python is a prorammin lanuae that lets you work quickly and interate systems more effectialy.

Метод c_str() - возвращает указатель на область памяти, в которой хранятся символы строки, возвращает значение типа char*. Возвращаемое значение можно рассматривать как C-строку и использовать в функциях, которые должны получать на вход C-строку.

Пример 4

Дан текст. Переставить в нем первую букву первого предложения и первую букву второго предложения (сначала найти номер первой точки, не использовать склейку +, find, erase, substr и т.д.).

#include <iostream>
#include <string>

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string txt;
std::cout << "Введите текст: ";
getline(std::cin, txt);
int i = 0, i_second = -1;
while (1) {
if (i >= txt.length()) break;
if (txt[i] == '.') {
i++;
while (txt[i] == ' ') i++;
i_second = i;
break;
}
i++;
}
char ch = txt[0];
if (i != 0 && i_second != -1) {
txt[0] = txt[i_second];
txt[i_second] = ch;
}
std::cout << "\nРезультат:" << std::endl;
std::cout << txt << std::endl;
}

Результаты работы программы:

Введите текст: Winter is coming.        Soon.

Результат:
Sinter is coming. Woon.

Пример 5

Дан текст. Переписать в другую переменную только цифры, латинские буквы и пробелы (использовать склейку +, не использовать find, erase, substr и т.д.).

#include <iostream>
#include <string>

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string txt, result = "";
std::cout << "Введите текст: ";
getline(std::cin, txt);
for (int i = 0; i < txt.length(); i++) {
if (txt[i] >= '0' && txt[i] <= '9') // если символ - цифра
result += txt[i];
if (txt[i] >= 'a' && txt[i] <= 'z') // если символ - строчная буква
result += txt[i];
if (txt[i] >= 'A' && txt[i] <= 'B') // если символ - заглавная буква
result += txt[i];
if (txt[i] == ' ') // если символ - пробел
result += txt[i];
}
std::cout << "\nРезультат:" << std::endl;
std::cout << result << std::endl;
}

Результаты работы программы:

Введите текст: все пройдет this too will pass 2021

Результат:
this too will pass 2021

Передача строк в качестве параметров функций

Переменные типа string передаются в функцию также, как переменные другого типа.

Пример 6

Создать функцию find поиска в строке символа. Функция принимает два параметра: строку и символ, а возвращает или позицию первого включения символа в строку, или -1, если символ в строке не найден.

Используя функцию find, проверить наличие символа @ в строке, заданной пользователем.

#include <iostream>
#include <string>

int find(std::string s, char c) {
for (int i = 0; i < s.length(); i++)
if (s[i] == c) return i;
return -1;
}

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
int n = 0;
std::string line;
char ch = '@';
std::cout << "Введите текст: " << std::endl;
getline(std::cin, line);
if (find(line, ch) != -1)
std::cout << "Есть";
else
std::cout << "Heт";
std::cout << std::endl;
}

Результаты работы программы:

Введите текст: 
al@aaaa.ru
Есть

Пример 7

Создать функцию find поиска в строке символа. Функция принимает два параметра: строку и символ, а возвращает или позицию первого включения символа в строку, или -1, если символ в строке не найден.

Используя функцию find, из текстового файла вывести на консоль только строки, в которых встречается символ @.

#include <fstream>
#include <iostream>
#include <string>

int find(std::string s, char c) {
for (int i = 0; i < s.length(); i++)
if (s[i] == c) return i;
return -1;
}

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
int n = 0;
std::string line;
char ch = '@';
std::fstream F("text.txt"); // открываем файл в режиме чтения
if (F) { // если открытие файла прошло корректно, то
// цикл для чтения значений из файла; выполнение цикла прервется,
// когда достигнем конца файла, в этом случае F.eof() вернет истину.
while (!F.eof()) {
getline(F, line); // чтение строки из потока F в line
if (find(line, ch) != -1) {
std::cout << line << std::endl; // вывод line на экран
}
}
F.close(); // закрытие потока
} else { // если открытие файла прошло некорректно, то вывод
// сообщения об отсутствии такого файла
std::cout << "Файл не существует" << std::endl;
}
}

Результаты работы программы:

Aaaaddad@lll

Пример 8

Дан текстовый файл. Запишите в другой файл только такие строки исходного файла, которые не содержат чисел.

Создадим функцию, которая принимает в качестве параметра строку, а возвращает или true, если в строке есть числа, или false, если числа в строке не найдены.

#include <fstream>
#include <iostream>
#include <string>

bool is_digit(std::string line) {
for (int i = 0; i < line.length(); i++)
if (line[i] >= '0' && line[i] <= '9') return true;
return false;
}

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string line;
std::ifstream in("text.txt"); // открываем файл в режиме чтения
std::ofstream out("result.txt"); // открываем файл в режиме записи
if (in && out) { // если открытие файлов прошло корректно, то
// цикл для чтения значений из файла и записи; выполнение цикла прервется,
// когда достигнем конца файла, в этом случае in.eof() вернет истину.
while (1) {
getline(in,
line); // чтение очередной строки из потока in в переменную line
if (is_digit(line)) {
out << line << std::endl;
std::cout << line << std::endl;
}
if (in.eof()) break; // выход из цикла, если конец файла достигнут
}
in.close(); // закрытие потока
out.close(); // закрытие потока
} else { // если открытие файла прошло некорректно, то вывод
// сообщения об отсутствии такого файла
std::cout << "Файл не существует" << std::endl;
}
}

Результаты работы программы:

weqwe231321
123adsad
12312sadasd
3dffdsf

Пример 9

Дан текстовый файл. Запишите в другой файл содержимое исходного файла, в начале каждой строки добавив e-mail:. Создадим функцию, которая принимает в качестве параметра строку, а возвращает строку, у которой в начале добавлено e-mail:..

#include <fstream>
#include <iostream>
#include <string>

std::string email(std::string line) {
line = "e-mail: " + line;
return line;
}

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string line;
std::ifstream in("text.txt"); // открываем файл в режиме чтения
std::ofstream out("result.txt");
if (in && out) { // открываем файл в режиме записи
// цикл для чтения значений из файла и
// записи; выполнение цикла прервется, когда достигнем конца файла, в этом
// случае in.eof() вернет истину.
while (1) {
getline(in,
line); // чтение очередной строки из потока in в переменную line
out << email(line) << std::endl;
std::cout << email(line) << std::endl;
if (in.eof()) break; // выход из цикла, если конец файла достигнут
}
in.close(); // закрытие потока
out.close(); // закрытие потока
} else { // если открытие файла прошло некорректно, то вывод
// сообщения об отсутствии такого файла
std::cout << "Файл не существует" << std::endl;
}
}

Результаты работы программы:

e-mail: Aaaaddad@lll
e-mail: weqwe231321
e-mail: 123adsad
e-mail: 12312sadasd
e-mail: 3dffdsf
e-mail: asdasdsadasd

Пример поиска подстроки

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

I. Исходные данные и результаты

Исходные данные:

  1. Текстовый файл неизвестного размера, состоящий из строк длиной не более 80 символов. Поскольку по условию переносы отсутствуют, можно ограничиться поиском заданной последовательности в каждой строке отдельно. Следовательно, необходимо помнить только одну текущую строку файла. Для ее хранения выделим строковую переменную line.
  2. Последовательность символов для поиска, вводимая с клавиатуры. Поскольку по условию задачи она не содержит пробельных символов, ее длина также не должна быть более 80 символов, иначе поиск завершится неудачей. Для ее хранения также выделим строковую переменную word.

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

Для работы с файлом потребуется служебная переменная соответствующего типа.

II. Алгоритм решения задачи

Для каждой строки проверять, содержится ли в ней заданная последовательность.

Печатать сообщение о наличии заданной последовательности или её отсутствии в каждой строке.

Если файл пустой, напечатать сообщение об отсутствии за¬данной последовательности и завершить программу.

III. Программа и тестовые примеры

#include <fstream>
#include <iostream>
#include <string>

int main() {
#ifdef WIN32
system("chcp 65001");
#else
setlocale(LC_ALL, "Russian");
#endif
std::string word, line; // 2
std::cout << "Введите слово для поиска: ";
std::cin >> word;
std::ifstream fin("text.txt"); // 3
if (!fin) {
std::cout << "Ошибка открытия файла." << std::endl;
return 1; // 4
}
int Nomer = 0; // 1
while (getline(fin, line)) { // 5
std::cout << Nomer;
if (line.find(word) == -1) { // 6
std::cout << " - отсутствует!" << std::endl;
} else {
std::cout << " - есть!" << std::endl;
}
Nomer++;
}
if (!Nomer) {
std::cout << "Отсутствует!" << std::endl;
}
}

Результаты работы программы:

Введите слово для поиска: we
0 - отсутствует!
1 - есть!
2 - отсутствует!
3 - отсутствует!
4 - отсутствует!
5 - отсутствует!

Рассмотрим помеченные операторы. В операторе 1 описывается переменная Nomer для хранения номера текущей строки. В операторе 2 описывается переменная line для размещения очередной строки файла и переменная word для размещения искомой последовательности символов.

В операторе 3 определяется объект fin класса входных потоков ifstream. С этим объектом можно работать так же, как со стандартными объектами cin и cout, то есть использовать операции помещения в поток << и извлечения из потока >>. Предполагается, что файл с именем text.txt находится в том же каталоге, что и текст программы, иначе следует указать полный путь, дублируя символ обратной косой черты, так как иначе он будет иметь специальное значение, например:

ifstream fin("с:\\prim\\cpp\\text.txt");  // 3

В операторе 4 проверяется успешность создания объекта fin. Файлы, открываемые для чтения, проверять нужно обязательно! В операторе 5 организуется цикл чтения из файла в переменную line.

Функция getline успешном чтении строки возвращает значение true, при достижении конца файла вернет значение false, завершающее цикл.

Для анализа строки в операторе 6 применяется метод line.find(word), который осуществляет поиск подстроки word в строке line. В случае успешного поиска функция возвращает указатель на найденную подстроку, в случае неудачи – значение -1.

В качестве тестового примера приготовьте текстовый файл, состоящий из нескольких строк. Для тестирования программы следует запустить ее по крайней мере два раза: введя с клавиатуры слово, содержащееся в файле, и слово, которого в нем нет. Файл должен быть в кодировке ANSI, иначе из-за разных кодировок слова, вводимого с клавиатуры, и текстового файла слово никогда не будет найдено в файле.

Даже такую простую программу мы рекомендуем вводить и отлаживать по шагам. Предлагаемая последовательность отладки:

  1. Ввести "скелет" программы (директивы #include, функцию main(), операторы 1-4). Добавить контрольный вывод введенного слова. Запустив программу, проверить ввод слова и успешность открытия файла. Выполнить программу, задав имя несуществующего файла, для проверки вывода сообщения об ошибке. Удалить контрольный вывод слова.

  2. Проверить цикл чтения из файла: добавить оператор 5 с его завершающей фигурной скобкой, внутри цикла поставить контрольный вывод прочитанной строки:

    std::cout << line << std::endl;

    Удалить контрольный вывод строки.

  3. Дополнить программу операторами проверки и вывода сообщений.