Тема: опрацювання рядків мовою C++.
Мета: сформувати поняття рядка у мові програмування, ознайомити учнів з їх функціями та методами, навчити створювати програми мовою C++ опрацюванням рядків.
Обладнання: ПК із встановленим ОС і середовищем програмування мовою C++ або стійким сполученням з Інтернетом для роботи з середовищем програмування мовою C++ у режимі
online.
Структура уроку
Хід уроку
1. Організаційний момент
Вітання з класом. Перевірка присутності і готовності учнів до уроку. Перевірка виконання домашнього завдання.
Мотивація навчання: на цьому уроці ви ознайомитеся з основними функціями та методами рядків, що передбачені у мові C++. Навчитеся писати програми опрацювання рядків тексту. Наприклад, з переведенням рядка в число та навпаки.
2. Актуалізація опорних знань
Рядки — упорядковані послідовності символів, що використовують для зберігання і представлення текстової інформації. За допомогою рядків можна працювати з усім, що може бути подано у текстовій формі.
Запис значення рядка здійснюють у лапках. Наприклад:
string s = "Hello World!"
Службові символи (екрановані послідовності)
тлумачення | код ASCII | |
\' | одинарні лапки | 0x27 |
\" | подвійні лапки | 0x22 |
\? | знак запитання | 0x3f |
\\ | зворотня коса риска | 0x5c |
\0 | нульовий символ | 0x00 |
\a | звуковий сигнал | 0x07 |
\b | повернення на один символ назад | 0x08 |
\f | нова сторінка | 0x0c |
\n | новий рядок | 0x0a |
\r | повернення каретки | 0x0d |
\t | горизонтальна табуляція | 0x09 |
\v | вертикальна табуляція | 0x0b |
3. Вивчення нового матеріалу
Символ — найменша складова тексту. Наприклад, літера латиниці, кирилиці, цифра десяткового запису, знак пунктуації тощо.
У мові програмування С++ передбачено використання символьних величин.
Символьна величина — це величина типу char для збереження (цілочисельного) коду символу.
У таблиці ASCII подано написання символів і їхні коди.
Значення символьної величини записують як символ в одинарних лапках, наприклад, 'a'.
Рядок у мові С++ подають (як один з варіантів) масивом елементів типу char, що закінчуються нуль-термінатором '\0'.
Такі рядки називають С рядками або рядками в стилі С. Вони мають тип char *. Назва рядка є сталим покажчиком на перший символ. Значення рядка записують у подвійних лапках. При оголошенні такого рядка необхідно враховувати наявність у кінці рядка нуль-термінатора, тобто відводити додатковий байт під нього.
При ініціалізації початковим значенням довжину рядка буде визначено автоматично, а в кінець рядка буде додано нуль-термінатор. При спробі вивести нуль-термінатор буде продубльовано попередній символ — див. приклад коду
#include <iostream> #include <string> using namespace std; int main() { char c ='a', s[10] = "123456789"; // при s[9] буде помилка компіляції char * t="abcdefghi"; cout<<c<<'*'<<endl<<s<<endl<<t<<endl<<">"<<t[10]<<"<"; }
та результату його роботи.
a* 123456789 abcdefghi >><
Функції для роботи з символами і рядками char *
strchr(s,c) — пошук першого входження символу c у рядку s. У разі вдалого пошуку повертає покажчик на місце першого входження символу с. Вказівка:
cout<<strchr(s,c);
виводить рядок s, починаючи з цього першого входження. Якщо символ не знайдено, то буде повернуто NULL;
strprbk(s,t) — повертає покажчик першого входження будь-якого символу рядка t в рядок s. Вказівка:
cout<<strpbrk(s,t);
виводить рядок s, починаючи з цього першого входження. Якщо символ не знайдено, то буде повернуто NULL;
#include <iostream> #include <cstring> #include <cstdio> using namespace std; int main() { char s[] ="little letters маленькі літери 123"; for (int j=0; j<strlen(s); j++) putchar(toupper(s[j])); return 0; }
та результат її дії
LITTLE LETTERS маленькі літери 123
Примітка. Оператор cout зчитує рядок до перших пробілу або ознаки кінця рядка. Для зчитування рядків, що містять пробіли використовують додаткові засоби мови С++. Наприклад, наступну функцію.
Функція getline призначена для введення даних з потоку до деякого роздільника, який не записують в отриманий масив даних. Виклик функції виглядає так:
cin.getline(s,n,d);
де s — зчитаний рядок типу char *, n — найбільша кількість символів, яку можна записати в рядок, d — роздільник, який вказує на кінець зчитуваного рядка. Останній параметр функції можна опустити. У цьому випадку буде використано роздільник як усталено '\n', породжуваний натисканням клавіші Enter.
Подамо приклад застосування цієї функції для зчитування рядка до знаку оклику з можливо кількома символами '\n', породженими натисканням клавіші Enter.
#include <iostream> using namespace std; int main () { char s[99]; cout <<"Введіть рядок, який завершується знаком оклику '!'"<< endl; cin.getline (s,99,'!'); cout<<s; }
Потоком введення може бути і зчитування з файлу.
#include <iostream> #include <fstream> using namespace std; int main () { char s[99]; ifstream f; f.open("input.txt"); f.getline (s,99,'!'); cout<<s; }
Функція getline має ще один варіант застосування — до рядкової змінної необмеженої довжини типу string, опис якої подано далі:
getline (f, s, d);
де f — потік даних, s — змінна для запису рядка, d — роздільник, який вказує на кінець рядка. Останній параметр функції можна не вказувати. У цьому випадку буде використано роздільник як усталено '\n'. Подамо приклад застосування цієї функції.
#include <string> #include <iostream> using namespace std; int main() { string s; cout <<"Введіть рядок, який завершується знаком оклику '!'"<< endl; getline (cin,s,'!'); cout<<s; }
Клас string було запроваджено як альтернативу типу char * для роботи з рядками. Щоб скористатися наявними можливостями цього класу, потрібно підключити бібліотеку string і простір назв std:
#include <string> using namespace std;
Оголошення змінної типу string здійснюють традиційно для С++ з можливою ініціалізацією.
string s; string t = "оголошення з ініціалізацією";
Переваги типу string у порівнянні з типом char *
можливість опрацювання рядків стандартними операторами C++ (=, +, == тощо);
забезпечення кращої надійності програмного коду. Наприклад, при копіюванні рядків, коли рядок-джерело має більший розмір ніж рядок-приймач;
використання рядка як самостійного типу даних для забезпечення несуперечливості даних.
Недолік типу string в порівнянні з типом char * — сповільнення швидкості опрацювання даних внаслідок того, що тип string — це контейнерний клас, робота з яким вимагає додаткових дій, прописаних у бібліотеці string.
Cтандартні (перевантажені) оператори класу string
тлумачення | |
= | надання значення |
+ | конкатенація (дописування) |
+= | надання значення з конкатенацією |
== | рівність (порівняння) |
!= | нерівність |
< | менше |
<= | не перевищує (менше або дорівнює) |
> | більше |
>= | не менше (більше або дорівнює) |
[] | індексація (як для масиву) |
Приклади ініціалізації змінних типу string
string s1 ("Привіт!"); string s2 = "Привіт!"; char * ps = "Привіт!"; string s3 (ps); string s4 (s3); string s5;
Надання значень змінним типу string можна здійснити:
Функція assign має кілька реалізацій:
Наступна частина коду подає приклади застосування цих реалізацій у порядку переліку:
string s0 = "abcdefgh", s1,s2,s3; char * p = "abcdefgh"; s1.assign(s0); // s1 = "abcdefgh" s2.assign(s0, 5, 2); // s2 = "fg" s3.assign(p, 5); // s3 = ""abcde"
Функція append — дописування у кінець рядка — має такі варіанти реалізації:
функція отримує посилання на рядок s, який додають (дописують) до об'єкту, що її викликає;
функція отримує покажчик на рядок типу const char *, яка завершується символом '\ 0'
— див. наступний код, у якому в коментарях записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string s0= "abc", s, t = "0123456"; char * p = "0123456"; s=s0; s.append(t); cout<<s<<endl; // abc0123456 s=s0; s.append(t,2,3); cout<<s<<endl; // abc234 s=s0; s.append(p); cout<<s<<endl; // abc0123456 s=s0; s.append(p,2); cout<<s<<endl; // abc01 s=s0; s.append(p,2,3); cout<<s<<endl; // abc234 return 0; }
Функція insert — надає можливість вставити у задану позицію рядка інший рядок повністю або лише частину — див. наступний код, у якому в коментарях записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string s = "abcdef", t = "123456"; s.insert(4, t); cout<<s<<endl; // abcd123456ef s.insert(2, t, 2, 4); cout<<s<<endl; // ab3456cd123456ef return 0; }
Функція replace виконує заміну символів одного рядка на інший або лише його частину. Перші два аргументи задають діапазон замінюваної частини — з якого індексу і якої довжини. Третій аргумент — рядок, на який або частину якого буде здійснено заміну. Четвертий і п'ятий (якщо їх записано) — діапазон частини третього аргумента, на який буде здійснено заміну — див. наступний код, у якому в коментарях записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string t = "abcdef", s,s0 = "0123456"; s=s0; s.replace (2,3,t); cout<<s<<endl; // 01abcdef56 s=s0; s.replace (2,6,t); cout<<s<<endl; // 01abcdef s=s0; s.replace (7,4,t); cout<<s<<endl; // 0123456abcdef s=s0; s.replace (2,7,t); cout<<s<<endl; // 01abcdef s=s0; s.replace (2,3,t,3,2); cout<<s<<endl; // 01de56 s=s0; s.replace (2,3,t,3,7); cout<<s<<endl; // 01def56 return 0; }
Функція clear видаляє всі символи з рядка.
#include <iostream> #include <string> using namespace std; int main() { string s = "0123456789"; s.clear(); cout << ">" << s << "<" << endl; // >< return 0; }
Функція erase видаляє символи з рядка. Вона має два аргументи: індекс (позиція), починаючи з якої потрібно видалити, і кількість символів, які видаляють. Якщо ці аргументи не вказано, то буде видалено усі символи — див. наступний код, у якому в коментарях записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string s = "0123456789"; s.erase (3, 5); cout<<s<<endl; // 01289 s.erase (); cout<<s<<endl; // return 0; }
Функції find і rfind повертають індекс позиції першого входження рядка-аргумента у даний рядок, починаючи з даного індекса-аргумента. Функції відрізняються напрямком пошуку:
У разі відсутності відповідного входження функції буде повернуто число за межами діапазону значень індексів елементів рядка, у якому здійснюють пошук. Пишуть, що буде повернуто –1, але буває інакше — див. наступний код, у якому в коментарях записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string s = "01234567890123456789", t = "345", u = "abcd"; cout<<s.find(t)<<endl; // 3 cout<<s.find(t,5)<<endl; // 13 cout<<s.find(t,15)<<endl; // 18446744073709551615 cout<<s.find(u)<<endl; // 18446744073709551615 cout<<s.rfind(t)<<endl; // 13 cout<<s.rfind(t,9)<<endl; // 3 cout<<s.rfind(t,2)<<endl; // 18446744073709551615 cout<<s.rfind(u)<<endl; // 18446744073709551615 return 0; }
Функція find_first_of(t,j) повертає індекс першої появи будь-якого з символів рядка t, починаючи позиції j. Якщо значення j не вказано, то вважають, що воно дорівнює 0, тобто пошук здійснюють з початку рядка.
Функція find_last_of(t,j) повертає індекс останньої появи будь-якого з символів рядка t, починаючи позиції j. Якщо значення j не вказано, то вважають, що воно дорівнює 0, тобто пошук здійснюють з початку рядка.
Функція find_first_not_of(t,j) повертає індекс першої появи символу, відмінного від символів рядка t, починаючи позиції j. Якщо значення j не вказано, то вважають, що воно дорівнює 0, тобто пошук здійснюють з початку рядка.
Функція find_last_not_of(t,j) повертає індекс останньої появи символу, відмінного від символів рядка t, починаючи позиції j. Якщо значення j не вказано, то вважають, що воно дорівнює 0, тобто пошук здійснюють з початку рядка.
#include<iostream> #include<string> using namespace std; int main() { string s = "01234567890123456789", t = "abc654"; cout<<s.find_first_of(t)<<endl; // 4 cout<<s.find_first_of(t,11)<<endl; // 14 cout<<s.find_last_of(t)<<endl; // 16 cout<<s.find_last_of(t,11)<<endl; // 6 cout<<s.find_first_not_of(t)<<endl; // 0 cout<<s.find_first_not_of(t,11)<<endl; // 11 cout<<s.find_last_not_of(t)<<endl; // 19 cout<<s.find_last_not_of(t,11)<<endl; // 11 }
Функція compare надає можливість порівнювати рядок або його частину з іншим рядком-параметром і має такі аргументи (перші два або вказують, або не вказують одночасно):
Функція compare працює таким чином:
— див. наступний код, у якому в коментарях записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string s = "01234", t = "43210", u = "0123432100"; cout<<s.compare(t)<<endl; // -1 cout<<t.compare(s)<<endl; // 1 cout<<s.compare(s)<<endl; // 0 cout<<s.compare(u)<<endl; // -5 cout<<u.compare(s)<<endl; // 5 cout<<u.compare(0,5,s)<<endl; // 0 cout<<s.compare(0,5,u)<<endl; // -5 cout<<u.compare(4,4,t)<<endl; // -1 cout<<u.compare(4,5,t)<<endl; // 0 cout<<u.compare(4,6,t)<<endl; // 1 return 0; }
Функція c_str повертає рядок типу char * і модифікатором const з тією самою послідовністю символів — див. наступний код, у якому в коментарі записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string s = "abcd"; const char * p; p = s.c_str(); cout<<p; // abcd return 0; }
Функції length і size повертають довжину рядка — див. наступний код, у якому в коментарі записано результат виведення:
#include <iostream> #include <string> using namespace std; int main() { string s = "abcdefg"; cout<<s.length()<<endl; // 7 cout<<s.size()<<endl; // 7 }
Функція push_back(c) додає до рядка символ c типу char.
#include <iostream> #include <string> using namespace std; int main() { string s = "Hello"; s.push_back('!'); cout<<s; // Hello! return 0; }
Функція resize(n) змінює довжину рядка на n. Якщо після n записати символ, то при збільшенні довжини рядка вільні місця буде заповнено цим символом:
#include <iostream> #include <string> using namespace std; int main() { string s = "Hello"; s.resize(8,'!'); cout<<s; // Hello!!! }
Функція substr(j) повертає підрядок даного рядка, починаючи з символу з індексом j, до кінця рядка. Якщо після j вказати необов'язковий параметр k, то буде повернуто підрядок довжини k даного рядка, починаючи з символу з індексом j.
#include <iostream> #include <string> using namespace std; int main() { string s = "0123456789"; cout<<s.substr(5)<<endl; // 56789 cout<<s.substr(2,5)<<endl; // 23456 }
На основі поданого вище опису можна створити програму, яка зчитує рядок і розбиває його на рядки за символами-роздільниками, які не приєднують до рядків результату. Така програма може мати застосування, наприклад, до опрацювання даних у форматі електронних таблиць csv з роздільником ';' як усталено. Використання вільно поширюванної бібліотеки boost дає можливість істотно спростити код такої програми. Але спочатку потрібно встановити цю бібліотеку. При ОС Ubuntu i Linux Mint для цього достатньо виконати таку вказівку Терміналу:
sudo apt-get install libboost-dev
При інших ОС за бібліотекою можна звернутися на офіційний сайт. Ця бібліотека досяжна з деяких online-сервісів, щонайменше, з rextester.com. При наявності бібліотеки boost можна використати таку програму:
#include <iostream> #include <vector> #include <boost/algorithm/string.hpp> using namespace std; using namespace boost; int main() { string s; // зчитаний рядок для розбиття vector <string> v; // масив рядків результату getline(cin,s); // зчитування рядка до '\n'. split(v, s, is_any_of(" /;")); // розбиття рядка for (int i=0; i<v.size(); i++) cout << v[i] << endl; return 0; }
Ця програма при введенні рядка
abc/efg;hij klm
розіб'є його на 4 рядки за трьома роздільниками ' ', '/' і ';', заданими в аргументі функції split, і виведе результат розбиття окремими рядками:
abc
efg
hij
klm
4. Інструктаж з ТБ
5. Закріплення вивченого матеріалу
Завдання 1. Скласти програму для підрахунку кількості слів у введеному з клавіатури рядку. Словом вважати довільну послідовність символів, відокремлену символами пробілів, яких немає на початку і в кінці рядка. Порівняти складену програму з демонстраційними розв'язаннями a і b.
Завдання 2. Скласти програму для підрахунку кількості входжень символа 'a' у зчитаний рядок s. Порівняти складену програму з демонстраційним розв'язанням.
6. Підбиття підсумків уроку
Виставлення оцінок.
7. Домашнє завдання
Завдання 3. Скласти програму для перевірки того, що записані малими літерами латиниці через пробіли у рядку, розташовано в алфавітному порядку. Порівняти складену програму з демонстраційним розв'язанням. Протестувати програму для рядка з використанням на початку слів українських літер, яких немає у російській абетці.
Завдання 4. Перекласти мовою C++ програму мовою Pascal для отримання нормальної диз'юнктивної форми булевої фукції. За бажанням зчитування даних можна зробити з клавіатури, а виведення на екран.
Завдання 5. Розв'язати задачу відбірково-тренувальних зборів 9.2. Хімія.
Текст упорядкував Олександр Рудик.