АШШумилкин А. О.ОАиП · ПиТПМ
ОАиП · Лекция 06

Лекция №6. Работа со строками в C++ и C#.

2 158 слов9 разделов0 иллюстраций
↓ Скачать Markdown

§6.1 Понятие строки и её роль в программировании

Строка

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

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

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

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

§6.2 Работа со строками в С++

В C++ поддержка строк реализована на нескольких уровнях. На низком уровне используются C-строки — массивы символов, завершаемые нулевым символом \0. Такой подход требует ручного управления памятью и уязвим к ошибкам, таким как переполнение буфера.

Для удобной и безопасной работы со строками в стандартной библиотеке C++ предусмотрен класс std::string, определённый в заголовке <string>.

Этот класс предоставляет широкий набор методов для выполнения типичных операций: конкатенации, сравнения, поиска, извлечения подстрок и изменения содержимого.

Объекты std::string управляют памятью автоматически, что упрощает разработку и повышает надёжность программ.

Использование std::string является современной и рекомендуемой практикой при работе с текстовыми данными в C++.

§6.2.1 С – строки и их ограничения

Символ

– элементарная единица, некоторый набор которых несет определенный смысл. В языке программирования С++ предусмотрено использование символьных констант.

Символьная константа

– это целочисленное значение, представленное в виде символа, заключённого в одинарные кавычки, например, 'a'. В таблице ASCII представлены символы и их целочисленные значения.

Строки в стиле С (C-style string)

— это частный случай массива символьных констант.

Вы уже видели несколько примеров таких строк в виде строковых литералов, когда писали такой код:

cpp

cout << "Hello World";

Это эквивалентно такому объявлению массива:

cpp

char SayHello[] = {'Н', 'е', 'l', 'l', 'о', ' ', 'W', 'o', 'г', 'l', 'd','\0'}; 
cout << SayHello << endl;

Обратите внимание

Последний символ в массиве — нулевой символ '\0'. Он также называется знаком окончания строки (string-terminating character), поскольку указывает компилятору, что строка закончилась. Такие строки в стиле С — это частный случай символьных массивов, последним символом которых всегда является нулевой символ '\0'. Когда вы используете в коде строковый литерал, компилятор сам добавляет после него символ '\0'.

Строка при объявлении может быть инициализирована начальным значением, например, так:

cpp

char SayHello[] = "Hello World"

При такой инициализации строки заключаются в двойные кавычки.

Компилятор автоматически выделяет память под 12 символов (11 букв и завершающий \0) и инициализирует массив. Доступ к отдельным символам осуществляется по индексу: str[0] = 'H';.

Для работы с символьными строками выделяется специальный набор функций. Например, с помощью функции gets(имя_переменной) считаются все введённые символы с пробелами до тех пор, пока во вводимом потоке не встретится код клавиши enter. Если использовать операцию cin то из всего введённого считается последовательность символов до первого пробела.

В таблице ниже представлены функции для работы со строковым типом:

ФункцияПояснение
strlen(имя_строки)определяет длину указанной строки, без учёта нуль-символа
Копирование строк
strcpy(s1,s2)выполняет копирование символов из строки s2 в строку s1
strncpy(s1,s2,n)выполняет копирование n символов из строки s2 в строку s1. возвращает значения s1
Конкатенация строк
strcat(s1,s2)объединяет строку s2 со строкой s1. Результат сохраняется в s1
strncat(s1,s2,n)объединяет n символов строки s2 со строкой s1. Результат сохраняется в s1
Сравнение строк
strcpm(s1,s2)сравнивает строку s1 со строкой s2 и возвращает результат типа int: 0 –если строки эквивалентны, >0 – если s1s2 С учётом регистра
strncmp(s1,s2,n)сравнивает n символов строки s1 со строкой s2 и возвращает результат типа int: 0 –если строки эквивалентны, >0 – если s1s2 С учётом регистра
stricmp(s1,s2)сравнивает строку s1 со строкой s2 и возвращает результат типа int: 0 – если строки эквивалентны, >0 – если s1s2 Без учёта регистра
strnicmp(s1,s2,n)сравнивает n символов строки s1 со строкой s2 и возвращает результат типа int: 0 – если строки эквивалентны, >0 – если s1s2 Без учёта регистра
Обработка символов
isalnum(c)возвращает значение true, если с является буквой или цифрой, и false в других случаях
isalpha(c)возвращает значение true, если с является буквой, и false в других случаях
isdigit(c)возвращает значение true, если с является цифрой, и false в других случаях
islower(c)возвращает значение true, если с является буквой нижнего регистра, и false в других случаях
isupper(c)возвращает значение true, если с является буквой верхнего регистра, и false в других случаях
isspace(c)возвращает значение true, если с является пробелом, и false в других случаях
toupper(c)если символ с, является символом нижнего регистра, то функция возвращает преобразованный символ с в верхнем регистре, иначе символ возвращается без изменений.
Функции поиска
strchr(s,c)поиск первого вхождения символа с в строке s. В случае удачного поиска возвращает указатель на место первого вхождения символа с.
strcspn(s1,s2)определяет длину начального сегмента строки s1, содержащего те символы, которые не входят в строку s2
strspn(s1,s2)возвращает длину начального сегмента строки s1, содержащего только те символы, которые входят в строку s2
Функции преображения
atof(s1)преобразует строку s1 в тип double
atoi(s1)преобразует строку s1 в тип int
atol(s1)преобразует строку s1 в тип long int
Функции стандартной библиотеки ввода/вывода <stdio>
getchar(с)считывает символ с со стандартного потока ввода, возвращает символ в формате int
gets(s)считывает поток символов со стандартного устройства ввода в строку s до тех пор, пока не будет нажата клавиша ENTER

Несмотря на простоту, C-строки имеют ряд существенных ограничений:

  • Ручное управление памятью. При работе с динамическими строками разработчик сам отвечает за выделение (new[]) и освобождение памяти (delete[]), что увеличивает вероятность утечек памяти.
  • Риск переполнения буфера. Функции стандартной библиотеки, такие как strcpy, strcat, gets, не выполняют проверку границ массива. Например:

cpp

char buffer[10];
strcpy(buffer, "This is too long"); // Переполнение — неопределённое поведение
  • Отсутствие встроенных методов. C-строки не являются объектами — для выполнения операций (сравнение, конкатенация, определение длины) необходимо использовать отдельные функции из <cstring>.

Из-за этих недостатков использование C-строк в современном C++ не рекомендуется в пользу безопасных и удобных альтернатив, таких как std::string.

§6.2.2 Класс std::string

Для удобной и безопасной работы со строками в C++ стандартная библиотека предоставляет класс std::string, определённый в заголовочном файле <string>. Подключение этого заголовка необходимо для использования класса:

cpp

#include <string>

Класс std::string представляет собой контейнер, управляющий строковыми данными с автоматическим выделением и освобождением памяти.

Он является частью Standard Template Library (STL) и реализует строку как динамически изменяемую последовательность символов.

Преимущества std::string перед C-строками:\

  • Автоматическое управление памятью — разработчику не нужно вручную выделять или освобождать память.
  • Безопасность — операции проверяют границы, исключая переполнение буфера.
  • Гибкость и удобство — богатый набор методов и перегруженных операторов.
  • Интеграция с STL — совместимость с алгоритмами и контейнерами стандартной библиотеки.

Объявляются и инициализируются строки с типом данных std::string следующим образом:

cpp

// Пустая строка
std::string s1;                    
// Инициализация строковым литералом
std::string s2 = "Hello";          
// Копирование строки
std::string s3(s2);         
// Конкатенация при инициализации       
std::string s4 = s2 + " world";

Переменные типа std::string можно легко копировать, присваивать и передавать в функции. Они уничтожаются автоматически при выходе из области видимости, что предотвращает утечки памяти.

Использование std::string является рекомендуемым подходом при работе со строками в современном C++, так как это гораздо удобнее, чем использовать С – строки.

§6.2.3 Основные операции со строками в С++

Класс std::string предоставляет широкий набор методов и перегруженных операторов для выполнения типичных операций обработки строк.

Ниже приведены ключевые операции с краткими пояснениями и примерами.

1. Конкатенация строк

Поддерживается с помощью операторов + и +=, что позволяет легко объединять строки:

cpp

std::string a = "Hello";
std::string b = " world";
std::string c = a + b;     // "Hello world"
a += "!";                  // a становится "Hello!"

2. Доступ к символам

Отдельные символы строки доступны по индексу с использованием оператора []. Индексация начинается с 0:

cpp

std::string s = "abc";
char ch = s[1];            // ch = 'b'
s[0] = 'x';                // s становится "xbc" 

3. Длина строки

Количество символов в строке возвращает метод length() или его синоним size():

cpp

std::string s = "text";
size_t len = s.length();   // len = 4

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

Операторы сравнения (==, !=, <, <=, >, >=) позволяют сравнивать строки лексикографически:

cpp

std::string a = "apple", 
            b = "banana";
if (a < b) 
{ 
    /* true */ 
}

Для более гибкого сравнения используется метод compare():

cpp

int result = a.compare(b); // Возвращает 0, >0 или <0

5. Поиск подстрок

Метод find() возвращает позицию первого вхождения подстроки или std::string::npos, если подстрока не найдена:

cpp

std::string s = "Hello world";
size_t pos = s.find("world"); // pos = 6 

Метод rfind() выполняет поиск с конца строки.

6. Извлечение подстроки

Метод substr() возвращает новую строку — фрагмент исходной:

cpp

std::string part = s.substr(6, 5); // "world"	

7. Модификация строки

  • insert(pos, str) — вставка строки в указанную позицию:

cpp

s.insert(5, ", "); // "Hello, world"
  • erase(pos, len) — удаление части строки:

cpp

s.erase(5, 2); // Удаляет 2 символа с позиции 5
  • replace(pos, len, str) — замена части строки:

cpp

s.replace(6, 5, "C++"); // "Hello C++"

§6.3 Работа со строками в С#

В языке C# строковый тип string является ссылочным типом данных и предназначен для хранения текстовой информации. Он реализован как класс System.String** в .NET-платформе и предоставляет богатый набор встроенных методов для обработки строк.

§6.3.1 Тип string и его особенности

В C# тип string представляет собой встроенный псевдоним для класса System.String и предназначен для хранения текстовых данных. Несмотря на синтаксическую простоту, string является ссылочным типом, а не значимым, что означает, что переменная типа string хранит ссылку на объект в управляемой памяти (куче).

Пример объявления и инициализации:

csharp

string name = "Alice";

При выполнении операции:

csharp

name = name + " Smith";

создаётся новый строковый объект с значением «Alice Smith», а ссылка name перенаправляется на него.

Старый объект («Alice») становится недостижимым и впоследствии удаляется сборщиком мусора.

Последствия неизменяемости:

  • Безопасность: строки могут безопасно передаваться между методами и потоками без риска неожиданного изменения.
  • Кэширование и интернирование: CLR автоматически кэширует строковые литералы (через механизм string interning), что позволяет экономить память при повторяющихся значениях.
  • Производительность: частые операции с изменением строк (например, в цикле) приводят к созданию множества временных объектов, что увеличивает нагрузку на память и сборщик мусора.

§6.3.2 Основные операции со строками в C#

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

1. Конкатенация строк

Объединение строк выполняется с помощью оператора + или статического метода string.Concat():

csharp

string firstName = "Alice";
string lastName = "Smith";
string fullName = firstName + " " + lastName;

// или
string fullName2 = string.Concat(firstName, " ", lastName);  

2. Интерполяция строк

Средство интерполяции (с префиксом $) позволяет встраивать выражения прямо в строковой литерал, делая код более читаемым:

csharp

string name = "Alice";
int age = 25;
string message = $"Hello, {name}! You are {age} years old.";

3. Доступ к символам

Отдельные символы строки доступны по индексу с помощью оператора []. Индексация начинается с 0:

csharp

char first = name[0];    // 'A'
char last = name[name.Length - 1]; // 'e'

Попытка доступа за пределами диапазона вызывает исключение IndexOutOfRangeException.

4. Длина строки

Количество символов в строке возвращает свойство Length:

csharp

int len = name.Length;   // 5

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

  • Оператор == сравнивает содержимое строк (не ссылки):

csharp

if (str1 == str2) 
{ 
    // ... 
}
  • Метод Equals() позволяет задать дополнительные параметры, например, игнорирование регистра:

csharp

str1.Equals(str2, StringComparison.OrdinalIgnoreCase)
  • Статический метод string.Compare() выполняет лексикографическое сравнение:

csharp

int result = string.Compare(str1, str2, true); 
// true — игнорировать регистр

6. Поиск подстрок

  • IndexOf() возвращает позицию первого вхождения подстроки или возвращает -1, если не найдено:

csharp

int pos = text.IndexOf("world");
  • Contains() проверяет наличие подстроки и возвращает bool:

csharp

1. bool found = text.Contains("Hello");

7. Извлечение подстроки

Метод Substring() возвращает часть строки, начиная с указанной позиции:

csharp

string part = text.Substring(6, 5); 
// с 6-го символа, 5 шт.

8. Преобразование регистра

  • ToUpper() — преобразует строку в верхний регистр.
  • ToLower() — преобразует в нижний регистр.

csharp

string upper = name.ToUpper(); // "ALICE"

9. Удаление пробелов

  • Trim() — удаляет пробелы (и другие пробельные символы) в начале и конце.
  • TrimStart(), TrimEnd() — удаляют только в начале или только в конце:

csharp

string clean = "  text  ".Trim(); // "text"

§6.3.3 Использование StringBuilder в C#

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

Для эффективной работы со строками, подвергающимися частым изменениям, в .NET предоставляется класс System.Text.StringBuilder. В отличие от string, StringBuilder является изменяемым (mutable) — он позволяет модифицировать содержимое строки без создания новых объектов.

StringBuilder необходимо использовать:

  • При построении строк в циклах (например, формирование отчёта, лога, HTML-кода).
  • При большом количестве операций конкатенации.
  • Когда важна производительность и оптимизация памяти.

Как и у любой конструкции, у StringBuilder есть основные методы:

  • Append(value) — добавляет значение в конец строки.
  • AppendLine(value) — добавляет значение и переход на новую строку.
  • ToString() — возвращает итоговую строку типа string.
  • Clear() — очищает содержимое.
  • Length, Capacity — позволяют управлять размером.

csharp

using System.Text;
 
StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.AppendLine(", world!");
sb.Append("Today is ");
sb.Append(DateTime.Now.ToShortDateString());
 
string result = sb.ToString();
// Результат: Hello
//            , world! Today is 09.08.2025"