У ранніх версіях С++ рядки розглядалися як символьні масиви. Для роботи з ними розроблено бібліотеку функцій string.h, що містить ефективні засоби для роботи з рядками. Згодом була розроблена стандартна бібліотека шаблонів Standard TemplateLibrary (STL), яка надає більш потужні засоби, об’єднані в клас string. Але незважаючи на існування цього окремого для рядків класу, символьні масиви, що закінчуються нульовим байтом ‘\0’, залишаються досить популярними. Це відбувається завдяки їх ефективності і можливості контролювання операції з рядками.
Для обробки символьних типів даних бібліотека функцій string.h має велику кількість вбудованих функцій, що збільшують продуктивність праці програмістів та скорочують час на розробку програм, наприклад:
- функції перевірки символів;
- функції перетворення символів;
- функції перевірки рядків;
- функції маніпулювання рядками.
Функції наводяться у вигляді списків, що згруповані за їх розташуванням у заголовних файлах. Найчастіше надаються прототипи функцій, що описують, як слід використовувати функції у програмах (див. розділ 9).
Далі розглянемо прототипи, стислий опис, дію та методику застосування основних функцій обробки даних символьного типу.
Функції копіювання рядків:
- char strcpy (s, *st); — виконує операцію копіювання байтів рядка st у рядок s(включаючи “\0”; повертає s), наприклад:
char str [50];
strcpy (str, “О деле суди по исходу.”);
- char *strdup (const char *str); — виконує копіювання рядка str і повертає покажчик на рядок-копію, наприклад:
char “st1 = “Слово — есть поступок.”;
char *st2;
st2 = strdup (st1); //копируется st1 в st2;
- char * strncpy (char *st1, const char *st2, int n); — виконує копіювання n символів з рядка st2 у st1 (рядок stl повинен бути більше або дорівнювати st2, інакше виникне помилка), наприклад:
char st1[ ] = “Паскаль “;
char st2[ ] = “Привет из далека “;
strnpy (st1, st2, 3); // st1 — “Прикаль“.
Функції, конкатенації рядків:
- char *strcat (char *st1, const char *st2); — поєднує st1 і st2 та повертає st1,наприклад:
char str [100];
strcpy (str, “Borland “);
strcat (str, ” C++5″);,
у результаті маємо рядок
string = “Borland C++5”;
- char *strncat (char *st1, const char *st2, int n); — додає до рядка st1 n символів рядка st2 і повертає знову в st1, наприклад:
char st1 [90] = “Привет “;
char st2 [50] = “студент и студентка”;
strncat (st1, st2, 7);,
у результаті маємо рядок:
st1 = “Привет студент ” .
Функції порівняння рядків:
- int strcmp (char *stl, char *st2); — порівнює рядки st1 і st2 та повертає цілу величину, що дорівнює:
<0 — якщо st1 < st2;
= 0 — якщо st1 = st2;
>0 — якщо st1 > st2;,
наприклад:
char st1[ ] = “Слово ” ;
char st2[ ] = “слово”;
int k;
k = strcmp (st1, st2); //k<0;
- int stricmp (const char *stl, const char *st2); — виконує порівняння рядків, не враховуючи регістра символів; повертає цілу величину, як і функція strcmp(),наприклад:
char st1[ ] = “Слово “;
char st2[ ] = “слово”;
int k;
k = stricmp (st1, st2); //k = 0;
- int strncmp (char *stl, char *st2, int n); — виконує порівняння рядків із заданою кількістю символів n у st1 і st2 і повертає цілу величину:
<0 — якщо st1 < st2;
=0 — якщо st1 = st2;
>0 — якщо st1 > st2; ;
- char *strnicmp (char *stl, char *st2, int n); — виконує порівняння рядків із заданою кількістю символів n у st1 і st2, незалежно від регістра, і повертає цілу величину, як і в попередньому випадку.
Функції перетворення символів рядка:
- char *strlwr (char*st); — перетворює символи рядка st верхнього регістра в символи нижнього регістра, при цьому інші символи не враховуються. Наприклад:
char st [ ] = ” Лазерный Принтер”;
strlwr (st); // st = ” лазерный принтер” ;
- char *strupr (char *st); — перетворює символи рядка st нижнього регістра в символи верхнього регістра, інші символи не враховуються;
- char *strrev (char *st); — записує символи в рядку st у зворотному порядку (реверсує рядок), наприклад:
char st [ ] = ” Hello”;
strrev (st); //st – ” olleH”;
- char *strchr (char *st, int c); — визначає перше входження символа с у рядок st;повертає покажчик на символ у рядку st, що відповідає введеному символу, наприклад:
char st [90] = ” Borland С++5 ”
char *spt;
spt= strchr (st, ‘+”); — тепер покажчик spt вказує на підрядок “++5” рядка st;
- char *strrchr (char *st, int c); — знаходить останнє входження символа с у рядок st;якщо символ с у рядку не виявлений, повертає 0, інакше повертає покажчик на останній символ у рядку st, що відповідає заданому зразку, наприклад:
char st [80] = “Borland С++5”;
char *spt;
spt= strrchr (st, ‘+’); — покажчик spt вказує на підрядок “+5” рядка st.
Функції пошуку підрядка в рядку:
- strspn (const char *st1, const char *st2 ); — повертає кількість символів від початку рядка st1, що збігаються із символами рядка st2, де б вони не знаходилися в st2,наприклад:
char st1 [ ] = “Borland С++5”;
char st2 [ ] = ” narlBod “;
int k;
k= strspn (sti, st2); — змінна k одержує значення, що дорівнює 8, тому що перші8 символів рядка містилися в st1(враховуючи символ пропуску);
- char *strstr (const char *st1, const char *st2); — функція шукає в рядку st1 перше входження st2 і повертає покажчик на перший символ, знайдений у st1, з підрядкаst2; якщо рядок st2 не виявлений в st1, функція повертає 0, наприклад:
char stl [ ] = “Привет, сокурсник, идем на экзамен”;
char st2[ ] = “сокурсник”;
char spt;
spt = strstr (stl, st2);
Результат виконання:
spt = “сокурсник, идем на экзамен”.
За потреби визначення останнього входження можна спочатку реверсувати рядок за допомогою функції strrew;
- char *strtok (char *st, const char *dlm); — розбиття рядка на лексеми (сегменти), обмежені символами, що входять до складу рядка dim. Цей параметр може містити будь-яку кількість різних обмежників — ознак границь лексем, після виділення лексеми в рядок st поміщається символ «\0».
Наступні виклики функції strtok() повинні бути з першим аргументом NULL. Бони будуть повертати покажчик на інші, наявні в st лексеми. Щоразу після завершення виділення лексеми у її кінці замість розділового символа поміщається символ «\0». Після того, як у рядку не залишиться жодної лексеми, функція повертає NULL. Для збереження вихідного рядка його треба записати в резервну змінну. Цю функцію зручно використовувати для розбиття речення на слова або будь-які інші сегменти. Розглянемо приклад програми з використанням функції strtok().
Приклад 7.1. Скласти програму, яка вводить речення, здійснює розбиття його на слова, підраховує кількість символів у кожному слові та виводить відповідну інформацію
// Р7 1.СРР — применение функции strtok() /* определение порядкового номера слова в предложении и подсчет количества символов в каждом слове */ #include <string.h> #include <iostream.h> #include <conio.h> voidmain (void) { char *tk, *spt=", .!"; char st[ ] = "Делай великое, не обещая великого."; cout << st<< endl; int і = 1; tk = strtok (st, spt); while (tk != NULL) { cout << і << " слово — " << tk << " — содержит " << strlen(tk) << " символов" << endl; tk = strtok(NULL, spt); і++;} getch (); // задержка экрана }
Результати виконання програми:
Делай великое, не обещая великого.
1 слово — Делай — содержит 5 символов
2 слово — великое — содержит 7 символов
3 слово — не — содержит 2 символов
4 слово — обещая — содержит 6 символов
5 слово — великого — содержит 8 символов
Процес розбиття речення на слова можна було б здійснити з використанням і такого програмного фрагмента:
tk = strtok (st < spt); // первый вызов функции
while (tk)
if ((tk = strtok(), spt) != 0) cout <<. . ..
Для видалення з рядка підрядка або символа із заданої позиції у бібліотеці string.h немає спеціальної функції, однак можна написати власну, наприклад:
void del (char *st, int k, int n);
{
for (int і = k; і < strlen(st); i++)
st[i] = st [i + n];
st[i] =’\0′; // запись “\0” в конец новой строки
}
де st — вихідний рядок (покажчик на нього);
n — кількість символів у підрядку, що вилучається;
k — позиція, з якої треба вилучити підрядок. Наведемо приклад, котрий ілюструє використання функції void del (); (детальніше про функції див. в розділі 9).
Приклад 7.2. Скласти програму вилучення підрядка в n символів з k-ої позиції в рядку.
/* Р7 2.СРР — удаление подстроки в п символов из k-ой позиции в строке */ #include <iostream.h> #include <string.h> #include <conio.h> //--------------- функция удаления подстроки из строки void del (char*sp, int k, int n) { int і; for (і = k; і < strlen(sp); і++) sp[i] = sp[i+n]; sp[i] = \0';} main () //----------- главная функция { char st[50], pst[10]; cout << "***** Введите строку\n"; cin.getline(st, 50); cout << "***** Введите подстроку\n"; сіn >> pst; cout << "Исходная строка: — "<< st << endl; del (st, strstr(st, pst)-st, strlen(pst)); cout << "Новая строка: — "<<st<<endl; getch(); }
Результата обчислень:
***** Введите строку
Люблю писать программы на языке С++!
***** Введите подстроку
писать
Исходная строка: — Люблю писать программы на языке С++!
Новая строка: — Люблю программы на языке С++!
- void* memchr (const void *st, int s, int n); — функція шукає символ “s“ у рядку *st довжиною n байт, тобто в блоці пам’яті, на який вказує покажчик st. Якщо символ sзнайдений, функція повертає покажчик на цей символ, а в протилежному випадку —повертає NULL;
- void* memcmp (const void *s1, const void *s2, n); і void* memicmp (const void *sl,const void *s2, int count); — функції порівнюють n байт з двох буферів, на початок яких указують s1 і s2.
Функції повертають значення
<0 — якщо s1 < s2;
=0 — якщо s1 = s2;
>0 — якщо s1 > s2;;
- char *strset (char *st, int ch, int n); і char *strset (char *st, int ch); — функції заповнюють рядок st символом ch і повертають покажчик на отриманий рядок, n —заповнює n символів рядка st.
Функції перетворення рядків у числа та чисел у рядки знаходяться у файлі stdlib.h:
- int atoi (const char *s); — перетворює рядок s у число типу int. Повертає отримане число 0, якщо зустрінеться символ, що не може бути перетворений. Рядок повинен містити число, наприклад, «2345», та мати таку структуру: [пропуски] [знак числа] [цифри];
- long atol (const char *s); — перетворює рядок s у число типу long int (аналогічна функції atoi.);
- double atof (const char *s); — перетворює рядок символів у число з плаваючою крапкою типу double. Якщо зустрічається символ, що не може бути перетворений, повертає 0. Оброблюваний рядок повинен мати таку структуру: [пропуски] [знак числа] [цифра.цифра] [літера е, Е, d або D] [знак порядку] [цифри порядку], наприклад, «-12345.123» або «-12.345123 ЕЗ»;
- char *ecvt (double vl, int n, int *dec, int *sign); — перетворює число vl у рядок символів, кількість яких дорівнює n символів цифр. Положення десяткової крапки від першої цифри числа повертається до змінної, на яку вказує dec. Знак числа повертається до змінної, на яку вказує sign. Якщо sign = 0, то число є додатним, інакше — від’ємним. Отриманий рядок зберігається у внутрішній пам’яті функції, покажчик повертається на початок сформованого рядка;
- char *fcvt (double vl, int n, int *dec, int *sign); — аналогічна до попередньої функції char *ecvt(), але якщо для функції ecvt параметр dec задає загальну кількість цифр, то для функції fcvt — кількість цифр після десяткової крапки;
- char *gcvt (double vl, int n, char *buf); — перетворює число vl у рядок, котрий поміщає в буфер, покажчик на початок якого є buf, n — число цифр у символічному записі перетвореного числа. Отриманий рядок містить символ знака числа і десяткової крапки, якщо число містить менше десяткових цифр, ніж n. У цьому випадку молодша цифра дробової частини відкидається. Якщо перетворене число не можна помісити в задану кількість цифр n, функція генерує символьний запис в експоненціальній формі із символом Е і знаком порядку. Функція повертає покажчик на початок сформованого рядка;
- strlen (st) — повертає довжину змінної st без нуль-термінала «\0».
Функції перевірки символів знаходяться у файлі ctype.h:
- isgraph (s) — повертає значення «істина» (1), якщо s є друкованим символом, і «неправда» (0), якщо s є пропуском або яким-небудь не відображуваним символом;
- isprint (s) — повертає значення «істина» (1), якщо s є друкованим символом, включаючи символ пропуску, і «неправда» (0) у всіх інших випадках;
- ispunct (s) — повертає значення «істина» (1), якщо s є знаком пунктуації (будь-який друкований символ, крім пропуску), і «неправда» (0) в інших випадках;
- isdigit (s) — повертає значення «істина» (1), якщо s є цифрою від 0 до 9, і «неправда» (0) в інших випадках;
- isalmim (s) — повертає значення «істина» (1) якщо s є цифрою або літерою (заголовною або строковою), і «неправда» (0) у всіх інших випадках (тобто перевіряє алфавітні та цифрові символи).
Функції перетворення символів:
- tolower (s) — перетворює символ s до нижнього регістра;
- toupper (s) — перетворює символ s до верхнього регістра;
- atoi (s) — перетворює рядок s до цілого числа;
- atol (s) — перетворює рядок s до довгого цілого;
- atof (s) — перетворює рядок s до числа з плаваючою крапкою.
Розглянемо приклади з використанням рядкових функцій.
Приклад 7.3. Ввести до пам’яті комп’ютера список прізвищ, які розташовані в будь-якому порядку, та відсортувати їх за алфавітом.
Розглянемо перший варіант (див. Р7_3_1.СРР) реалізації поставленої задачі. Будемо вважати, що вводять прізвища та ініціали, тоді програма може мати вигляд:
// Р7 3 1.СРР — отсортировать фамилии по алфавиту #include <iostream.h> #include <string.h> #include <conio.h> main( ) { const int n=5; char sp[n][l5], r[15]; int i, k; //------------------------------- ввод фамилий и инициалов cout<< "***** Введите " << n << " фамилий \n"; for (і = 0; і < n; і++) { cout<<"Введите "<<(і+1)<<" фамилию и инициалы\n"; cin.getline (sp[i], sizeof (sp[i]) - 1); } //------------------------------ сортировка списка фамилий for (k = 1; k < n; k++) for (i = 0; і < n-k; i++) if (strcmp (sp[i], sp[i+l])>0) { strcpy (r, sp[i]); strcpy (sp[i], sp[i+1]); strcpy (sp[i+1], r);} cout<<"\n Отсортированный массив фамилий \n"; for (і = 0; і < n; i++) cout << sp[i] << endl; getch (); }
Результати обчислень:
******* Введите 5 фамилий
Введите 1 фамилию и инициалы
Иванченко С. И.
Введите 2 фамилию и инициалы
Авдиенко А. Р.
Введите 3 фамилию и инициалы
Яшин Б. Ю.
Введите 4 фамилию и инициалы
Кашкин Т. Б.
Введите 5 фамилию и инициалы
Мельниченко Т. Ю.
Отсортированный массив фамилий
Авдиенко А. Р.
Иванченко С. И.
Кашкин Т. Б.
Мельниченко Т. Ю.
Яшин Б. Ю.
У наведеній програмі використано масив прізвищ sp [6][15] і символьний рядок r, який потрібен для тимчасового зберігання прізвища при сортуванні масиву. Для сортування був застосований раніше розглянутий метод виштовхування («пухирця») (див. розділ 6.4).
Порівняння елементів символьного масиву (char sp[n][15]) здійснюється за допомогою функції strcmp( ), а перезапис прізвищ з одного елемента масиву sp[i] в другий — sp[i+1]— за допомогою функції strcpy( ) і змінної r.
Після сортування на екран виведено одержаний масив. Другий варіант (див.Р7_3_2.СРР) розв’язання поставленої задачі використовує покажчики.
/* Р7_3_2.СРР — сортировка списка фамилий в алфавитном порядке с использованием указателей */ #include <iostream.h> #include <string.h> #include <conio.h> void main( ) { const n=5; char sp [n][15]; int i, k; char *ps[n], *ptr; //ps[n] — массив указателей // ввод фамилий и инициализация массива указателей cout << "***** Введите фамилии \n"; for (і = 0; і < n; і++) { gets (sp [і]); ps[i] = sp[i];} //--------------------------- вывод исходной информации cout << "\n***** Исходный список\n"; for (і = 0; і < n; і++) puts (ps[i]); //------------------------------------ сортировка массива for (k = 1; k < n; k++) for (i = 0; i<n-k; i++) if (strcmp (ps[i], ps[i+1]) > 0) { ptr = ps[i]; ps[i] = ps[i+1]; ps[i+1] = ptr; } //--------------------- вывод отсортированного массива cout << "\n\n*****Отсортированный список \n"; for (і = 0; і < n; і++) puts (ps[i]); getch (); }
Результати виконання програми:
***** Введите фамилии
Игнатенко А. P.
Головко Н. А.
Долбня Б. В.
Андриенко С. Ф.
Ичко Т. В.
***** Исходный список
Игнатеяко А. Р.
Головко Н. А.
Долбня Е. В.
Андриенко С. Ф.
Ичко Т. В.
***** Отсортированный список
Андриенко С. Ф.
Головко Н. А.
Долбня Е. В.
Игнатенко А. Р.
Ичко Т. В.
Приклад 7.4. Ввести рядок і видалити в ньому зайві пропуски.
// Р7 4.СРР — удаление лишних пробелов в строке #include <iostream.h> #include <string.h> #include <conio.h> void main() { char st[ ] = "Краткость — сестра таланта"; int і, n = 0; //n — для подсчета пропусков for (і = 0; і < strlen(st); i++) { if (st[i] != ' ') { cout « st[i]; n=0; } else n++; if (n == 1) cout << st[i]; } getch(); }
Результати виконання програми:
Краткость — сестра таланта
Приклад 7.5. Визначити позицію входження підрядка в рядок.
// Р7 5.СРР — поиск позиции вхождения подстроки в строку #include <iostream.h> #include <string.h> #include <conio.h> const int m = 50; void main() { char *pt, mainstr[m], substr[m]; int n, k = 0; cout << "***** Введите строку " << endl; cin.getline (mainstr, m); cout << "***** Введите подстроку " << endl; cin.getline (substr, m); pt = strstr (mainstr, substr); cout << endl; while (pt) { k++; // номер вхождения n = pt - mainstr; cout << k <<" -oe вхождение подстроки" <<" номер позиции = "<< n << endl; pt = strstr(++pt, substr); cout << k << " " << *pt << endl;} if (k == 0) cout <<"Подстрока не содержится в строке"<<endl; getch(); }
Результат виконання:
***** Введите строку
Почти во всех делах самое трудное — начало.
***** Введите подстроку
самое трудное
1-ое вхождение подстроки номер позиции = 20
Приклад 7.6. Знайти заданий символ у рядку.
// Р7 6.СРР — поиск символа в строке #include <iostream.h> #include <string.h> #include <conio.h> main( ) { const int m = 50; char sim, *pt, str[m]; int n, k = 0; cout << "***** Введите строку" << endl; cin.get (str, m); cout << "***** Введите символ" << endl; сіn >> sim; pt = strchr (str, sim); while (pt) { k++; n = pt-str; cout<<k<<"-я позиция вхождения символа = "<<n<<endl; pt = strchr(++pt, sim); } cout << "Количество вхождений = "<<k<<endl; if (k==0) cout << "Символ не входит в строку" << endl; getch(); }
Результати обчислень:
***** Введите строку
Алгоритм — фундаментальное понятие информатики
***** Введите символ
и
1-я позиция входения символа = 5
2-я позиция входения символа = 32
3-я позиция входения символа = 35
4-я позиция входения символа = 43
5-я позиция входения символа = 45
Количество вхождений = 5
Приклад 7.7. З уведеного списку прізвищ (без ініціалів) вилучити такі, що починаються на задану літеру і мають задане закінчення, та вивести повідомлення про прізвище з найменшою кількістю літер.
/* P7_7.CPP — найти фамилии, начинающиеся на заданную букву и имеющие заданноеокончание */ #include <iostream.h> #include <string.h> #include <conio.h> main() { const int n = 6; char spis [n][15], pok[5], p; int i, minfam, k=0; //------------------------------ ввод списка фамилий без инициалов cout << "***** Введите "<<n<<" фамилий\n"; for (і = 0; і < n; i++) сіn >> spis [і]; cout << "***** Введите первую букву\n"; сіn >> р; cout << "**** Введите окончание\n"; сіn >> роk; // определение фамилии на заданную букву и на заданное окончание cout << "***** Искомые фамилии\n"; for (і = 0; і < n; і++) if (spis [i][0]==p && strcmp(strrchr(spis[i], pok[0]), pok)==0) cout<<spis [i]<<endl; //-------------- поиск фамилии с наименьшим количеством букв minfam - strlen (spis [0]); for (і = 1; і < n; і++) if (strlen (spis [і]) < minfam) { minfam = strlen (spis[i]); k=і; } соut<<"\Фамилия с наименьшим колич. букв — "<<spis [k]<<" \n"; cout << "Её длина = " << strle(spis [k]) << " символов\n"; getch (); }
Результати обчислень:
***** Введите 6 фамилий
Андриенко
Коваленко
Степаненко
Курко
Коноваленко
Перекотиполе
***** Введите первую букву
К
***** Введите окончание
ко
***** Искомые фамилии
Коваленко
Курко
Коноваленко
Фамилия с наименьшим колич. букв — Курко
Её длина = 5 символов
У програмі для зберігання першої літери використовується змінна з ім’ям р, а для закінчення — змінна роk. Вилучення закінчення прізвища здійснює функція strrchr(spis[i],pok[0]), яка повертає покажчик на останнє входження заданої літери (рок[0]) у рядок(spis[i]). Потім цей покажчик порівнюється з введеним закінченням прізвища за допомогою функції strcmp(). У цілому в операторі if… визначається як перша літера, так і закінчення прізвища. Змінна minfam існує для знаходження прізвища з найменшою кількістю літер згідно з алгоритмом визначення мінімального елемента масиву (див. приклад 1.3).