Досега ви показахме основите на езика C++. Забелязахте ли, че масивите са с фиксирана дължина. Текста представяхме с масиви от тип char[], което е доста ограничаващо наследство от езика C. Самия език C е много близък до асемблера и много далеч от съвременните езици за програмиране. C++ скъсява тази разлика но остава близо до машината. За това в C++ няма истински фундаментален тип данни представящ динамичен текстов масив. Това става чрез класа std::string. Той е част от стандартната библиотека идваща с вашия компилатор. Тя се нарича STL: Standard Template Library.
Хората разработили C++ наистина се сериозни и не обичат да повтарят вече написан код. STL се превежда на български като: Стандартна Библиотека от Шаблони. Името говори само за себе си. Почти всичко в библиотеката на вашия компилатор е шаблон. За това вашия компилатор е толкова любопитен и мощен. За да ви предостави най – бързия и сигурен код в инструкциите на вашия процесор, той се опитва да разреши всички възможни бъдещи проблеми по време на компилацията. Но това не винаги е възможно.
Какво е std::string?
Това е клас, вид структура от данни. Класа е обединение от някакви данни и функции които управляват поведението на данните. Всичко е събрано в едно. В нашия случай std::string класа е събрал в едно типа данни char[] и функции за управление на този масив от char. Функциите прикрепени към даден клас обработват собствените си данни и се наричат член – функции. Защо не методи? Така е възприето. Това са просто синоними.
std::string предоставя удобен и безопасен начин за работа с текст. Представлява динамичен масив от тип char. Заедно с член – функциите за работа с този текст. Масива char[] винаги е с точно определена дължина. Обикновено се започва с char[16]. Когато текста е по дълъг се заема памет от ОС, наречена динамична памет. И всеки път заеманата памет е два пъти по голяма (x2) и стария масив се копира в новозаетата памет от ОС. А стария масив се връща на ОС.
Така работят всички динамични структури от данни, не само масивите. Искам да кажа, че винаги работим с масив с фиксирана дължина и при нужда можем да заемем още памет. Сега сме 2026 година. Компютрите са много по бързи от преди 20 години. Така, че дали се ползва динамично разпределена памет от ОС или локалната памет разпределена от ОС за програмата ни това има нищожно влияние върху бързодействието.
Създаване на променлива от тип string
Класа string е проектиран да се държи като всяка фундаментална променлива. И винаги предоставя инициализиран текстов масив. Завършващ на ‘\0’. Когато не сме посочили стойност при декларацията string връща „“, празен низ. Както е показано No* променливите долу са „“.
#include <iostream>
#include <string> // не ме забравяй :)
using namespace std;
int main() {
string myName {"info"}; // инициализация
string NO_Name {}; // няма инициализация -> ""
string NONO_Name; // няма инициализация -> ""
cout << "[" << myName << "]" << endl;
cout << "[" << NO_Name << "]" << endl;
cout << "[" << NONO_Name << "]" << endl;
}За да използвате std::string трябва да добавите този заглавен файл от STL библиотеката.
#include <string> Слепване на низове (concatenation)
Това става с оператора +
string first = "Hello";
string second = "User";
string result = first + " " + second;string може да се слепва с други string, текстови литерали и други константи или масиви от тип char[]
#include <iostream>
#include <string>
using namespace std;
const char* prompt = " Welcome to OS terminal";
int main() {
string first = "Hello";
string second = "User";
string result = first + " " + second + prompt;
cout << result << endl;
}Колко е дълъг текстовия низ?
string str = {"Welcome to OS terminal"};
cout << str.length() << endl; // 23
cout << str.size() << endl; // 23length() член – функцията връща броя на символите без да брои нулевия терминатор ‘\0’ . Същото прави и size().
Достъп до символ от string
Без проверка на индекса имаме достъп със string[index]. Но ако проверяваме индекса използваме функцията at, string.at(index).
string word = "Code";
cout << word[0]; // 'C'
cout << word.at(1); // 'o'Проверката с at e нищожен товар за съвременен компютър, таблет или смартфон. At проверява въведения от вас индекс. И ако е извън обхвата вдига изключение. За изключенията ще учим много по късно. Просто го споменавам, за да знаете че има и такова нещо. Изключенията позволяват да обработвате грешки по време на изпълнение на програмата без да товарите с това ОС.
Обхождане на string
Кратка форма:
string text = "Hello Info";
for(char c : text) {
std::cout << c << " ";
}Или дългата форма:
#include <iostream>
#include <string>
using namespace std;
int main() {
string text = "Hello Info";
for(int i = 0; i < text.length(); i++) {
std::cout << text[i] << " ";
}
}Вмъкване на символ
Това става с член функцията insert.
string text = "Helo";
text.insert(3, "l"); // "Hello"Вмъква буквата ‘l’ на индекс 3.
0 1 2 3 4 5
H e l o
|
вмъква тук 'l'
и става:
0 1 2 3 4 5
H e l l o Можем да добавим и друг низ. Не само буква.
std::string a = "Hello world";
std::string b = "C++ ";
a.insert(6, b); // a става: "Hello C++ world"Премахване на символи от string
Изтриването на символи става с член функцията erase.
erase(index, n) изтрива n символа започвайки от позиция index.
string text = "Hello";
text.erase(1, 2); // "Hlo"Какво се случва:
0 1 2 3 4 5
H e l l o
___
|
това се изтрива
резултат
0 1 2 3 4 5
H l oТърсене в низ
Има няколко начина на търсене на текст или символ в std::string.
Търсене на текст (подниз). Това става с член функцията find. Тя връща позицията на първото срещане. Или връща константа string::npos което означава, че нищо не е намерено.
#include <iostream>
#include <string>
using namespace std;
int main() {
string text = "Hello world";
size_t pos = text.find("world");
if (pos != std::string::npos) {
cout << "Намеренo на позиция: " << pos << endl;
} else {
cout << "Не е намерено" << endl;
}
}Търсене от дадена позиция
// търси "world" след индекс 5
size_t pos = text.find("world", 5); Последно срещане (rfind)
Търси отзад напред.
size_t pos = text.rfind("world");По кратка проверка
if (text.find("Hello") != std::string::npos) {
// съдържа
}
// от C++ 23
if (text.contains("Hello")) {
// съдържа
}Търсене на символ
size_t pos = text.find('o');string.substr()
Функцията substr() извлича част от низа, започвайки от дадена позиция и обхващайки определена дължина. Връща нов string.
#include <iostream>
#include <string>
using namespace std;
int main() {
string str = "example";
// Започва от индекс 1, взема 3 символа
string sub = str.substr(1, 3);
cout << "Substring: " << sub << endl; // xam
}string.append()
string.append() добавя текст в края на стринга. На практика е подобно на +=, но дава повече контрол. Кода е по ясен.
#include <iostream>
#include <string>
int main() {
std::string s = "Hello";
s.append(" world");
std::cout << s; // Hello world
}Добавяне на друг string.
std::string a = "Hello";
std::string b = " C++";
a.append(b); // "Hello C++"Добавяне на символ.
std::string s = "Hi";
s.append(3, '!'); // "Hi!!!"Важно:
Всички варианти връщат референция към стринга → може да се chain – ва. Да се направи верига:
s.append(„Hello“).append(“ world“);
empty(), push_back() и pop_back()
Тези функции работят с единични символи в края на низа. push_back() вмъква символ в края. pop_back() премахва последната буква от стринга. Функцията empty() проверява дали низа е празен и връща булева стойност.
#include <iostream>
#include <string>
int main() {
std::string s;
// Проверка дали е празен
if (s.empty()) {
std::cout << "Стрингът е празен\n";
}
// Добавяме символи
s.push_back('A');
s.push_back('B');
s.push_back('C');
std::cout << "След push_back: " << s << std::endl; // ABC
// Премахваме символи
if (!s.empty()) {
s.pop_back(); // маха 'C'
}
std::cout << "След pop_back: " << s << std::endl; // AB
}Класа string не разполага с методи които правят горните операции в началото на низа. Защото това са тежки действия. Но ако наистина ви трябват можете да си ги напишете.
#include <iostream>
#include <string>
void stringPushFront(std::string& s) {
s.insert(0, 1, 'A');
}
void stringPopFront(std::string& s) {
if(!s.empty()) s.erase(0, 1);
}
int main() {
std::string s = "BC";
stringPushFront(s);
std::cout << s << std::endl; // ABC
stringPopFront(s);
std::cout << s << std::endl; // BC
}Моля забележете, че в този пример използвахме референции, за да избегнем копирането на евентуално големи блокове памет.
Функцията replace()
Синтаксис: replace(pos, length, new_text)
Премахва текст започващ от индекса pos и с дължина length. И на негово място вмъква new_text.
#include <iostream>
#include <string>
int main() {
std::string s = "Hello world";
s.replace(6, 5, "C++");
std::cout << s << std::endl; // Hello C++
}#include <iostream>
#include <string>
int main() {
std::string s = "I like Java";
std::string t = "C++";
s.replace(7, 4, t); // "I like C++"
std::cout << s << std::endl;
}string.clear()
Това нулира съдържанието на низа до „“.
#include <iostream>
#include <string>
int main() {
std::string str {"hello"};
std::cout << "[" << str << "]" << std::endl;
str.clear();
std::cout << "[" << str << "]" << std::endl;
}Функциите starts_with() и ends_with()
Тези функции са налични за std::string от C++ 20. С тях можете да определите дали стринга започва с даден префикс. Или завършва с някакъв суфикс.
Проверява дали низът започва с даден текст: starts_with()
#include <iostream>
#include <string>
int main() {
std::string text = "Hello world";
if (text.starts_with("Hello")) {
std::cout << "Започва с Hello\n";
}
}Проверява дали низът завършва с даден текст: ends_with()
#include <iostream>
#include <string>
int main() {
std::string text = "Hello world";
if (text.ends_with("world")) {
std::cout << "Завършва с world\n";
}
}Проверява дали низът завършва с даден текст.
Сравнение на низове
Когато сравняваме strings сравнението е лексикографско. Сравняват се символ по символ. По точно се сравняват числените стойности на типа char.
string a = "apple";
string b = "banana";
if (a == b) { } // проверка за равенство
if (a < b) { } // лексикографско сравнениеПовече за std::string можете да научите от тук.
На този сайт можете да намерите пълна и точна информация за актуалното състояние на STL и стандартите на езиците C++ и C. Сайта се подържа от професионални програмисти и е само място за справка. Не е официален или свързан с комитета по стандартизация на C++.
Трябва да се знае:
std::string съхранява данни от тип char. Char е в основата на съвременното кодиране на текст. Char е един байт. И всички кодировки на Unicode работят с типа string. Това означава, че C++ string може да съхранява всякакъв текст. Но това не значи, че можете да редактирате текста директно.
Често срещани грешки
- Забравен #include <string> Понякога кода ни работи, защото друга инклудната библиотека от STL го е добавила;
- Винаги работете със std::string. Това ви дава гъвкавост;
- Не ви препоръчвам да използвате [] без проверка. Използвайте функцията at;
Заключение
std::string е основен инструмент в C++, който значително улеснява работата с текст. Вместо ръчно управление на масиви от символи, той предлага безопасност, удобство и мощни възможности.
Да обобщим член функциите които научихме:
- insert/erase: Вмъкване и изтриване;
- find/rfind: Търсене;
- append: Вмъкване в края;
- push_back/pop_back: Работим с низа като стек;
- replace: Замяна на текст с друг текст;
- clear(): Връща променливата до празен низ;
Задачи
1. Нека потребителя въведе някакъв дълъг текст. Изведете неговата дължина и боря на гласните и съгласните от въведения текст. Използвайте функцията getline() вместо >>.
#include <iostream>
#include <string>
int main() {
std::string text;
std::getline(std::cin, text);
...
}getline чете всичко до натискането на Enter. >> чете като пропуска интервалите в началото и чете една дума до първия срещнат интервал.
Решение
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
// броене на гласни
int countVowels(const string& text) {
int count = 0;
for (char c : text) {
char lower = tolower(c);
if (isalpha(lower)) {
if (lower == 'a' || lower == 'e' || lower == 'i' ||
lower == 'o' || lower == 'u' || lower == 'y') {
count++;
}
}
}
return count;
}
// броене на съгласни
int countConsonants(const string& text) {
int count = 0;
for (char c : text) {
char lower = tolower(c);
if (isalpha(lower)) {
if (!(lower == 'a' || lower == 'e' || lower == 'i' ||
lower == 'o' || lower == 'u' || lower == 'y')) {
count++;
}
}
}
return count;
}
int main() {
string text;
cout << "Въведи текст: ";
getline(cin, text);
cout << "Дължина: " << text.length() << endl;
cout << "Гласни: " << countVowels(text) << endl;
cout << "Съгласни: " << countConsonants(text) << endl;
}2. Потребителя е въвел някакъв текст (латиница). Направете всички букви главни.
Решение
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
void toUpperCase(string& text) {
for (char& c : text) {
c = toupper(c);
}
}
int main() {
string text;
cout << "Въведи текст: ";
getline(cin, text);
toUpperCase(text);
cout << "Резултат: " << text << endl;
}3. Потребителя е въвел някакъв текст (латиница). Направете всички малки букви главни. А главните малки.
Решение
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
void swapCase(string& text) {
for (char& c : text) {
if (islower(c)) {
c = toupper(c);
} else if (isupper(c)) {
c = tolower(c);
}
}
}
int main() {
string text;
cout << "Въведи текст: ";
getline(cin, text);
swapCase(text);
cout << "Резултат: " << text << endl;
}4. Потребителя е въвел число. Използвайте „cin >>„. По префикса определете какво е числото: десетично, бинарно, осмично или шеснайсетично. Не трябва да проверявате самото число.
Решение
#include <iostream>
#include <string>
using namespace std;
bool decimalTest(const string& s) {
if (s.empty()) return false;
switch(s[0]) {
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': {
return true;
// break; -- нямаме нужда от това
// излизаме от функцията с отговор
}
}
return false;
}
int main() {
string input {};
cin >> input;
if (input.starts_with("0x") || input.starts_with("0X")) {
cout << "Шеснайсетично" << endl;
exit(0);
}
if (input.starts_with("0b") || input.starts_with("0B")) {
cout << "Бинарно" << endl;
exit(0);
}
if (input.starts_with("0")) {
cout << "Осмично" << endl;
exit(0);
}
if(decimalTest(input)) {
cout << "Десетично" << endl;
exit(0);
}
cout << "Това е текст" << endl;
exit(1);
}5. Напишете програма която чете няколко реда текст въведен от потребителя. Четенето спира при въведен празен ред. Запишете целия въведен текст в променлива. След което направите следните замествания: „{“ -> „begin“, „}“ -> „end“. Запишете ги в променливата. Отпечатайте променливата.
Решение
#include <iostream>
#include <string>
using namespace std;
int main() {
string line;
string text;
// Четене на редове до празен ред
while (true) {
cout << ">> ";
getline(cin, line);
if (line.empty()) break;
text += line + "\n";
}
// Заместване на "{" с "begin"
size_t pos = 0;
while ((pos = text.find("{", pos)) != string::npos) {
text.replace(pos, 1, "begin");
pos += 5; // дължината на "begin"
}
// Заместване на "}" с "end"
pos = 0;
while ((pos = text.find("}", pos)) != string::npos) {
text.replace(pos, 1, "end");
pos += 3; // дължината на "end"
}
// Отпечатване
cout << text << endl;
}6*. Потребителя въвежда няколко реда текст. Четенето спира при първия празен ред въведен от потребителя. Напишете програма, която изброява броя на въведените думи.
Решение
#include <iostream>
#include <cctype>
using namespace std;
int main() {
string line;
int wordCount = 0;
while (true) {
cout << ">> ";
getline(cin, line);
if (line.empty()) {
break; // спира при празен ред
}
bool inWord = false;
for (char ch : line) {
if (!isspace(ch)) {
if (!inWord) {
wordCount++; // започва нова дума
inWord = true;
}
} else {
inWord = false; // край на дума
}
}
}
cout << "Брой думи: " << wordCount << endl;
}Как работи накратко:
- Четем редове с
getline(); - Спираме при въведен празен ред;
- Обхождаме низа символ по символ;
isspace(ch)проверява дали е интервал, символа за табулация или друго което се класифицира като празно пространство и не се отпечатва в терминала;- Броим дума само когато преминаваме от празно пространство към видим символ;