Розробка уроку

Тема: опрацювання рядків мовою C++.

Мета: сформувати поняття рядка у мові програмування, ознайомити учнів з їх функціями та методами, навчити створювати програми мовою C++ опрацюванням рядків.

Обладнання: ПК із встановленим ОС і середовищем програмування мовою C++ або стійким сполученням з Інтернетом для роботи з середовищем програму­вання мовою C++ у режимі online.

Структура уроку

  1. Організаційний момент.
  2. Актуалізація опорних знань.
  3. Вивчення нового матеріалу.
  4. Інструктаж з ТБ.
  5. Закріплення вивченого матеріалу.
  6. Підбиття підсумків уроку.
  7. Домашнє завдання.

Хід уроку

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 *

бібліотека cstring

Примітка. Оператор 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 *

Недолік типу 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дописування у кінець рядка — має такі варіанти реалізації:

— див. наступний код, у якому в коментарях записано результат виведення:

#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. Хімія.


Текст упорядкував Олександр Рудик.