C++

Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна

Методи зміни вмісту рядка: assign(), append(), push_back(), insert(), erase(), replace(), resize(). Витяг підрядка через substr(). Порівняння рядків: оператори та метод compare().

Модифікація std::string

Рядок — не масив, він вміє «самостійно» змінюватись

Подивіться на цей код і спробуйте передбачити вивід:

ModifyPuzzle.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "Hello, World!";

    s.replace(7, 5, "C++");
    s.insert(0, ">>> ");
    s.erase(s.length() - 1, 1);

    cout << s << "\n";

    return 0;
}
./ModifyPuzzle
$ ./MutationPuzzle
>>> Hello, C++
Execution finished with exit code 0.

Три виклики методів — і рядок повністю перетворено. У цій статті ми розберемо весь арсенал методів модифікації std::string: від простого присвоювання до точкової заміни підрядка.


Присвоювання: assign

Оператор = — найпростіший спосіб замінити вміст рядка. Метод .assign() пропонує ті ж операції, але з більшими можливостями:

Assign.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s;

    // Присвоїти C-рядок
    s.assign("Hello");
    cout << s << "\n"; // Hello

    // Присвоїти n копій символу
    s.assign(5, '*');
    cout << s << "\n"; // *****

    // Присвоїти підрядок іншого рядка: assign(str, pos, count)
    string src = "Hello, World!";
    s.assign(src, 7, 5); // з позиції 7, 5 символів
    cout << s << "\n"; // World

    // Присвоїти повністю інший рядок
    s.assign(src);
    cout << s << "\n"; // Hello, World!

    return 0;
}
./Assign
$ ./Assign
Hello
*****
World
Hello, World!
Execution finished with exit code 0.
.assign() повертає std::string& — посилання на сам рядок. Це дозволяє ланцюжок викликів: s.assign("X").append("Y"). Усі методи модифікації, що повертають std::string&, підтримують таке «fluent»-з'єднання.

Додавання в кінець: append та push_back

Оператор += і метод .append() — синоніми для більшості випадків. Різниця: .append() надає ширший набір форм виклику.

Append.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "Hello";

    // Один символ — push_back або += char
    s.push_back('!');
    cout << s << "\n"; // Hello!

    // C-рядок
    s.append(" World");
    cout << s << "\n"; // Hello! World

    // n копій символу
    s.append(3, '.');
    cout << s << "\n"; // Hello! World...

    // Підрядок іншого рядка: append(str, pos, count)
    string extra = "---[END]---";
    s.append(extra, 3, 5); // "[END]"
    cout << s << "\n"; // Hello! World...[END]

    // += — той самий результат, але коротше
    s += " OK";
    cout << s << "\n"; // Hello! World...[END] OK

    return 0;
}
./Append
$ ./Append
Hello!
Hello! World
Hello! World...
Hello! World...[END]
Hello! World...[END] OK
Execution finished with exit code 0.
.pop_back() — зворотня операція до push_back(): видаляє останній символ. Пара push_back / pop_back перетворює std::string на простий стек символів. UB на порожньому рядку.

Вставка: insert

.insert() додає символи у довільну позицію рядка. Всі символи після точки вставки зсуваються вправо:

Insert.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "HelloWorld";

    // Вставити рядок за індексом
    s.insert(5, ", ");
    cout << s << "\n"; // Hello, World

    // Вставити n копій символу за індексом
    s.insert(0, 3, '>');
    cout << s << "\n"; // >>>Hello, World

    // Вставити підрядок: insert(pos, str, from, count)
    string tag = "[IMPORTANT]";
    s.insert(3, tag, 0, 11);
    cout << s << "\n"; // >>>[IMPORTANT]Hello, World

    return 0;
}
./Insert
$ ./Insert
Hello, World
>>>Hello, World
>>>[IMPORTANT]Hello, World
Execution finished with exit code 0.
Вставка у середину рядка вимагає зсуву всіх символів праворуч від точки вставки. Для рядка довжиною n і вставки у позицію p — складність O(n − p). Якщо потрібно часто вставляти на початок або в середину, розгляньте std::deque<char> або збір шматків у вектор рядків з подальшим з'єднанням.

Видалення: erase

.erase() видаляє символи з довільної позиції:

Erase.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = ">>>Hello, World<<<";

    // erase(pos, count) — видалити count символів з позиції pos
    s.erase(0, 3); // видалити ">>>" на початку
    cout << s << "\n"; // Hello, World<<<

    s.erase(s.length() - 3, 3); // видалити "<<<" в кінці
    cout << s << "\n"; // Hello, World

    // erase(pos) — видалити від pos до кінця
    s.erase(5);
    cout << s << "\n"; // Hello

    // erase() без аргументів — очистити весь рядок
    s.erase();
    cout << "'" << s << "'" << "\n"; // ''
    cout << "empty: " << boolalpha << s.empty() << "\n"; // true

    return 0;
}
./Erase
$ ./Erase
Hello, World<<<
Hello, World
Hello
''
empty: true
Execution finished with exit code 0.
.clear() — скорочений синонім .erase(). Обидва знуляють довжину, але не обов'язково звільняють виділену пам'ять (ємність лишається). Якщо потрібно також звільнити пам'ять — виклик s.clear() + s.shrink_to_fit().

Заміна підрядка: replace

.replace(pos, count, ...) видаляє count символів з позиції pos і на їх місце вставляє новий текст:

Replace.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "I love cats and cats are great";

    // replace(pos, count, newStr) — замінити один фрагмент
    s.replace(7, 4, "dogs");
    cout << s << "\n"; // I love dogs and cats are great

    // Замінити всі входження — через цикл з find()
    string from = "cats";
    string to   = "dogs";

    size_t pos = s.find(from);
    while (pos != string::npos)
    {
        s.replace(pos, from.length(), to);
        pos = s.find(from, pos + to.length()); // шукаємо далі
    }
    cout << s << "\n"; // I love dogs and dogs are great

    // replace з n копіями символу: replace(pos, count, n, ch)
    s.replace(0, 1, 3, '!');
    cout << s << "\n"; // !!! love dogs and dogs are great

    return 0;
}
./Replace
$ ./Replace
I love dogs and cats are great
I love dogs and dogs are great
!!! love dogs and dogs are great
Execution finished with exit code 0.

Зміна розміру: resize

.resize(n) змінює довжину рядка до n символів:

  • якщо n < length() — рядок обрізається;
  • якщо n > length() — рядок розширюється, нові символи заповнюються '\0' (або вказаним символом).
Resize.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "Hello";

    // Обрізання
    s.resize(3);
    cout << s << "\n"; // Hel

    // Розширення з заповнювачем за замовчуванням ('\0')
    s.resize(7);
    cout << s.length() << "\n"; // 7 (але є '\0' всередині!)

    // Розширення з власним символом-заповнювачем
    s.assign("Hello");
    s.resize(8, '!');
    cout << s << "\n"; // Hello!!!

    return 0;
}
./Resize
$ ./Resize
Hel
7
Hello!!!
Execution finished with exit code 0.
Після resize(n) з n > length() рядок містить нульові байти '\0' між старим вмістом і кінцем. Такий рядок матиме length() == n, але при виводі через cout символи після першого '\0' не відображатимуться — тому що потік зупиняється на нулі. Якщо потрібні бінарні дані з нулями — передавайте s.data() і s.length() явно.

Витяг підрядка: substr

.substr(pos, count) повертає нову копію рядка, що містить count символів починаючи з позиції pos:

Substr.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "Hello, World!";

    // substr(pos, count)
    string hello = s.substr(0, 5);  // "Hello"
    string world = s.substr(7, 5);  // "World"

    cout << hello << "\n"; // Hello
    cout << world << "\n"; // World

    // substr(pos) — від pos до кінця
    string tail = s.substr(7);
    cout << tail << "\n"; // World!

    // Типовий патерн: витягнути розширення файлу
    string filename = "report.2026.pdf";
    size_t dot = filename.rfind('.');       // остання крапка
    string ext = filename.substr(dot + 1);
    cout << ext << "\n"; // pdf

    return 0;
}
./Substr
$ ./Substr
Hello
World
World!
pdf
Execution finished with exit code 0.
.substr() завжди копіює символи — це O(n) за часом і пам'яттю. Якщо копія не потрібна — передайте std::string_view(s.data() + pos, count) (C++17). string_view — лише «вікно» у існуючий буфер без копіювання.

Порівняння рядків

Оператори ==, !=, <, >, <=, >=

std::string підтримує всі оператори порівняння. Порівняння лексикографічне — за кодами символів зліва направо:

Compare.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string a = "apple";
    string b = "banana";
    string c = "apple";

    cout << boolalpha;

    cout << (a == c) << "\n"; // true
    cout << (a != b) << "\n"; // true
    cout << (a < b)  << "\n"; // true  ('a' < 'b')
    cout << (b > a)  << "\n"; // true

    // Порівняння зі C-рядком — теж працює
    cout << (a == "apple") << "\n"; // true

    // Увага: порівняння регістрозалежне
    string upper = "Apple";
    cout << (a == upper) << "\n"; // false ('a' != 'A')

    return 0;
}
./Compare
$ ./Compare
true
true
true
true
true
false
Execution finished with exit code 0.

Метод .compare() — тричастинне порівняння

.compare() повертає int: негативне якщо *this < other, нуль якщо рівні, позитивне якщо *this > other. Основна перевага — можливість порівнювати підрядки:

CompareMethod.cpp
#include <iostream>
#include <string>

using namespace std;

int main()
{
    string s = "Hello, World!";

    // Порівняти підрядок з окремим рядком
    // s.compare(pos, count, other)
    int r1 = s.compare(7, 5, "World"); // "World" == "World" → 0
    int r2 = s.compare(0, 5, "World"); // "Hello" vs "World" → від'ємне
    int r3 = s.compare(7, 5, "Alpha"); // "World" vs "Alpha" → позитивне

    cout << r1 << "\n"; // 0
    cout << (r2 < 0 ? "менше" : "не менше") << "\n"; // менше
    cout << (r3 > 0 ? "більше" : "не більше") << "\n"; // більше

    // Порівняти підрядок з підрядком іншого рядка
    string other = "Hello, Planet!";
    int r4 = s.compare(0, 7, other, 0, 7); // "Hello, " == "Hello, " → 0
    cout << r4 << "\n"; // 0

    return 0;
}
./CompareMethod
$ ./CompareMethod
0
менше
більше
0
Execution finished with exit code 0.
Оператори ==/</> зручніші для загальних порівнянь. Метод .compare() потрібний коли необхідно порівняти частину рядка без виклику .substr() (а отже — без копіювання).

Порівняння без урахування регістру

У стандартній бібліотеці C++ немає готового compareIgnoreCase. Найпростіше рішення — привести обидва рядки до одного регістру перед порівнянням, або використати std::equal з функцією-предикатом:

CaseInsensitive.cpp
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>

using namespace std;

bool equalsIgnoreCase(const string& a, const string& b)
{
    if (a.length() != b.length()) return false;
    return equal(a.begin(), a.end(), b.begin(),
        [](unsigned char ca, unsigned char cb)
        {
            return tolower(ca) == tolower(cb);
        });
}

int main()
{
    cout << boolalpha;
    cout << equalsIgnoreCase("Hello", "hello") << "\n"; // true
    cout << equalsIgnoreCase("Hello", "HELLO") << "\n"; // true
    cout << equalsIgnoreCase("Hello", "World") << "\n"; // false

    return 0;
}
./CaseInsensitive
$ ./CaseInsensitive
true
true
false
Execution finished with exit code 0.

Зведена таблиця методів модифікації

s.assign(str)
string&
Замінити весь вміст рядка. Форми: assign(str), assign(n, ch), assign(str, pos, count).
s.append(str)
string&
Додати в кінець. Форми: append(str), append(n, ch), append(str, pos, count). Еквівалент +=.
s.push_back(ch)
void
Додати один символ в кінець. pop_back() — видалити останній.
s.insert(pos, str)
string&
Вставити у позицію pos. Форми: insert(pos, str), insert(pos, n, ch), insert(pos, str, from, count).
s.erase(pos, count)
string&
Видалити count символів з позиції pos. Без аргументів — очистити весь рядок.
s.replace(pos, count, str)
string&
Замінити count символів з позиції pos на str. Розміри можуть відрізнятись.
s.resize(n)
void
Змінити довжину до n. Скорочення обрізає, розширення заповнює '\0' (або вказаним символом).
s.substr(pos, count)
string
Повернути копію підрядка. count за замовчуванням — до кінця рядка.
s.clear()
void
Скинути довжину до 0. Ємність не зменшується.

Практика

Рівень 1 — Форматування імені

Напишіть програму, що зчитує прізвище та ім'я користувача (двома рядками) і виводить їх у форматі "Прізвище І." (скорочення імені до першої літери з крапкою).

Рівень 2 — Видалення зайвих пробілів

Напишіть функцію normalize(std::string s), що повертає рядок, у якому: всі послідовності пробілів замінено одним пробілом, а пробіли на початку та в кінці видалено.

Рівень 3 — Просте шаблонне заповнення

Напишіть функцію fillTemplate(std::string tmpl, const std::string& name, const std::string& subject), що замінює всі входження {name} і {subject} у шаблоні tmpl відповідними значеннями і повертає готовий рядок.


Резюме

assign та append

assign() замінює весь вміст; append() і += додають у кінець. push_back(ch) / pop_back() — стекоподібний доступ до кінця рядка. Всі форми: рядок, n символів, підрядок іншого рядка.

insert та erase

insert(pos, ...) вставляє у довільну позицію (O(n)). erase(pos, count) видаляє зазначений фрагмент. clear() скидає довжину до 0 без звільнення ємності.

replace та substr

replace(pos, count, newStr) видаляє count символів і вставляє новий текст на їх місце. substr(pos, count) повертає нову копію підрядка. Для читання без копіювання — std::string_view.

resize

resize(n) змінює довжину. Обрізає при n < length(), розширює (заповнює '\0' або вказаним символом) при n > length(). Не плутати з reserve() — той змінює ємність, не довжину.

Порівняння

Оператори ==, !=, <, > — лексикографічне порівняння. .compare(pos, count, other) — для порівняння підрядків без копіювання. Без регістру — std::equal з лямбдою або std::tolower.

Ланцюжок викликів

Методи assign, append, insert, erase, replace повертають std::string&. Це дозволяє ланцюжок: s.assign("X").append("Y").insert(0, ">>> "). Читабельно, без зайвих копій.

Що далі? Наступна стаття — пошук у рядку: методи find, rfind, find_first_of, find_last_of, find_first_not_of, find_last_not_of, а також знайомство з std::string_view — легковагим «вікном» у рядок без копіювання.

Copyright © 2026