C _Учебник_МОНУ
.pdf
Символи і рядки |
249 |
Приклад 7.10 Увести рядок і на його базі створити новий рядок з однолітерних слів.
Текст програми у C++ Builder:
void __fastcall TForm1::Button1Click(TObject *Sender) {int N=Edit1->Text.Length();
char *s=new char [N]; char *s1=new char [N]; AnsiString S=Edit1->Text;
strcpy(s, S.c_str()); |
|
strcpy(s1, ""); |
|
char *D=" .,?!-"; |
// Розділювачі |
char *p=new char [N]; |
|
p=strtok(s,D); |
// Читання першого слова |
while(p!=0) |
// Допоки не кінець рядка, |
{if(strlen(p)==1) |
// перевіряється розмір слів і однолітерні слова |
{strcat(s1,p); |
// долучаються до рядка s1 |
strcat(s1," "); |
// разом з пробілом |
} |
|
p=strtok(NULL,D); |
// Виокремлення наступного слова |
} |
|
Edit2->Text=AnsiString(s1);
delete []s; delete []s1; delete []p;
}
Приклад 7.11 Увести рядок і вивести всі слова, які розпочинаються і закінчуються на однакову літеру.
Розв‟язок. Для виокремлення слів рядка вважатимемо ознакою кінця слова пробіл. Оскільки після останнього слова здебільшого пробіл є відсутній, тому долучаємо його до рядка s у заздалегідь зарезервоване місце.
Рухаючись рядком, почергово виокремлюємо слова і запам‟ятовуємо у змінній p індекс першої літери (початку) поточного слова. Виокремлення слова можна реалізувати за допомогою функції strtok() або, як у наведеному нижче тексті програми, функції strncpy(). Ця функція копіює частину рядка s до рядка slovo, розпочинаючи з індексу p. Кількість літер, яку треба скопіювати, тобто довжину слова можна задати різницею індексів віднайденого пробілу і першого символу i-p. Після цього неодмінно слід долучити до слова завершальний нуль. Якщо у слові перша і остання літери збігаються, відбудеться виведення слова окремим рядком до компонента Memo.
Текст програми у C++ Builder:
void __fastcall TForm1::Button1Click(TObject *Sender) {int i, n=Edit1->Text.Length(); char* s=new char[n+2];
char* slovo=new char[n+1]; strcpy(s, Edit1->Text.c_str());
Символи і рядки |
251 |
Створювання функцій опрацювання символьних масивів
Досить часто при опрацюванні рядків доводиться здійснювати набори подібних команд для реалізації однакових дій, наприклад, для введення і виведення рядків, вилучення частини рядка тощо. У таких ситуаціях, а також для спрощення програмного коду і полегшення сприйняття програми, ці набори команд організовують як функції. Передавання рядків до функції є подібним до передавання масиву в якості параметра, при цьому треба зазначити адресу початку масиву (див. п. 5.2.4). Зробити це можна трьома способами:
float fun (int *a); float fun (int a[ ]); float fun (int a[25]);
За першого способу передається вказівник як посилання на масив, явно визначений в основній програмі. Більш докладно роботу з вказівниками розглянуто в розд. 6. У другій синтаксичній формі константний вираз у квадратних дужках є відсутній. Така форма може бути використана лише тоді, коли масив ініціалізується чи оголошується як формальний параметр. За третього способу явно зазначено кількість елементів масиву, що накладає обмеження на довжину опрацьовуваного рядка і, як наслідок, обмежує застосування цієї функції.
В якості прикладу організуємо функцію show_string() для виведення рядка на екран, параметром якої є рядок для виведення:
#include <iostream.h>
void show_string(char s[])
{cout << s << endl;
}
int main()
{show_string("Привіт, C++!");
char *st="Я вчуся програмувати мовою C++"); show_string(st);
cin.get();
}
Оскільки нуль-символ свідчить про кінець рядка, функція show_string() не потребує додаткового параметра, який би задавав кількість символів у рядку.
Наведемо ще один приклад функції, яка обчислюватиме довжину рядка. І хоча є подібна бібліотечна функція, для кращого розуміння організації рядків не зайвим буде створення власної функції для реалізації цього алгоритму. Нижченаведена функція length() для визначення кількості символів шукає символ '\0' у рядку. Тип функції, тобто її результату є int, а оператор return повертає у якості результату довжину рядка, яка обчислюється як номер останнього символу рядка '\0'.
#include <iostream.h> int length(char *s)
{int i;
//У циклі перебираються усі символи, допоки не зустрінеться '\0' for(i=0; s[i] != '\0'; i++);
Символи і рядки |
253 |
Функції перетворювання рядка char* на число
Виконати перетворювання рядка на число можна багатьма способами. Перший – використання бібліотечних функцій atoi(), atof(), atol(). Ці функції входять до стандартної бібліотеки мови С і мають такий формат:
int atoi(const char* str); long atol(const char* str); double atof(const char* str);
Результатами перетворювання цих функцій є числа відповідного типу. Функції цілочисельного перетворювання atoi() і atol() сприймають такий формат перетворюваного рядка:
[пробіли][знак]цифри
а функція перетворювання рядка в дійсне число atof(), відповідно:
[пробіли][знак][цифри][.цифри][{d | D | e | E }[знак]цифри]
Тут пробіли – будь-який зі знаків пробілу, табуляції ('\t'), вертикальної табуляції ('\v') – при перетворюванні ігноруються. Знак – символ '+' чи '-' (якщо його не зазначено, то вважається, що число є додатне). Цифри – символи від '0' до '9'. Для числа з рухомою крапкою, якщо немає цифр до символу '.', має бути хоча б одна цифра після нього. Дійсне число може бути подано в експоненціальній формі з одним із символів-префіксів експоненти.
Основний недолік цих функцій полягає в тому, що вони ніяк не сигналізують про помилку, якщо така станеться в перебігу перетворювання рядка на число, наприклад через невідповідність його формату чи то з інших причин. Цю проблему розв‟язують такі стандартні бібліотечні функції перетворювання рядків на числа:
long strtol(const char* str, char **endptr, int radix)
unsigned long strtoul(const char* str, char* *endptr, int radix) double strtod(const char* str, char* *endptr)
Ці функції мають такі відмінності від попередньої групи:
через параметр endptr вони повертають вказівник на перший символ, який не може бути інтерпретований як частина числа;
відбувається контроль переповнення і, якщо це сталося, функції сиг-
налізують встановленням змінної errno у значення ERANGE, а також поверта-
ють, відповідно, LONG_MAX/LONG_MIN, ULONG_MAX/ULONG_MIN та +/-HUGE_VAL за-
лежно від знака числа у переданому рядку;
функція strtol() використовує інформацію про поточні встановлені через setlocale регіональні налагодження, а, отже, може коректно інтерпретувати числа з символом ',' як розділювачем цілої та дробової частини;
для функцій strtol() і strtoul() можна задавати параметром radix основу системи числення, при цьому, якщо radix дорівнює 0, основа визначається автоматично за першими символами числа.
Типове використання цих функцій може мати вигляд:
char* end_ptr;
long val = strtol(str, &end_ptr, 10);
Символи і рядки |
255 |
Результати роботи програми:
23,5+6 Error
23.5-7.6
23.5-7.6=15.9
7.2.2 Клас AnsiString (String)
AnsiString (String) – тип динамічного рядка, який може містити до (232–1) символів. Цей тип рядків є зручним в опрацюванні, хоча і не є стандартним типом С++. Він з‟явився у Borland C++ Builder з Delphi, а отже, його можна використовувати лише у C++ Builder. Особливістю цього типу є те, що два рядки можуть фізично займати одну й ту саму ділянку пам‟яті. Рядок AnsiString містить лічильник посилань до нього і, коли цей лічильник набуває нульового значення, рядок автоматично знищується. На відміну від рядків типу char*, нумерація символів у рядку AnsiString розпочинається з 1.
Оголосити рядок AnsiString можна в такий спосіб:
AnsiString S;
Доволі зручним є введення та виведення рядків AnsiString за допомогою компонентів форми, оскільки відповідні текстові поля (властивості) компонентів мають цей тип. Наприклад, введення та виведення рядка S через компонент Edit1 можна подати в такий спосіб:
S=Edit1->Text; |
// Введення рядка з Edit1 |
Edit2->Text=S; |
// Виведення рядка до Edit2 |
Щоб визначити кількість символів рядка, використовується функція
Length():
int N=S.Length();
Найбільш поширені у застосовуванні функції та методи опрацювання рядків AnsiString наведено у табл. 7.4.
|
|
|
Таблиця 7.4 |
|
Деякі функції та методи опрацювання рядків AnsiString |
||
|
|
|
|
Назва |
|
Призначення |
Формат |
Length() |
|
повертає довжину рядка |
int Length(); |
|
|
(без урахування символу |
|
|
|
завершення рядка) |
|
SetLength() |
змінює довжину рядка на newLength, |
AnsiString& SetLength |
|
|
|
за потреби скорочуючи його |
(int newLength); |
Insert() |
|
вставляє рядок str, розпочинаючи |
AnsiString& Insert(const |
|
|
з індексу index |
AnsiString &str, int index); |
|
|
|
|
Delete() |
|
вилучає з рядка зазначену кількість |
AnsiString& Delete |
|
|
символів count, розпочинаючи |
(int index, int count); |
|
|
з індексу index |
|
256 |
|
Розділ 7 |
|
|
|
|
|
|
|
Закінчення табл. 7.4 |
|
|
Назва |
Призначення |
Формат |
|
|
Pos() |
повертає номер індексу, з якого |
int Pos(const AnsiString |
|
||
|
|
розпочинається підрядок subStr. |
&subStr); |
|
|
|
|
Якщо рядок не містить subStr, |
|
|
|
|
|
функція повертає 0 |
|
|
|
Trim() |
вилучає початкові й кінцеві пробіли |
AnsiString Trim(); |
|
||
|
|
і повертає новий рядок без пробілів |
|
|
|
TrimLeft() |
вилучає початкові пробіли, розташо- |
AnsiString TrimLeft(); |
|
||
|
|
вані ліворуч рядка |
|
|
|
TrimRight() |
вилучає кінцеві пробіли, розташовані |
AnsiString TrimRight(); |
|
||
|
|
праворуч рядка |
|
|
|
SubString() |
повертає підрядок, який містить |
AnsiString SubString |
|
||
|
|
count символів, розпочинаючи |
(int index, int count); |
|
|
|
|
з індексу index |
|
|
|
ToInt() |
перетворює рядок на ціле число |
int ToInt(); |
|
||
ToDouble() |
перетворює рядок на дійсне число |
double ToDouble(); |
|
||
LowerCase() |
перетворює символи рядка на малі, |
AnsiString LowerCase(); |
|
||
|
|
тобто на нижній регістр |
|
|
|
UpperCase() |
перетворює символи рядка на великі, |
AnsiString UpperCase() |
|
||
|
|
тобто на верхній регістр |
const; |
|
|
c_str() |
перетворює рядок до типу char* |
char* c_str(); |
|
||
|
За приклад використання деяких з наведених у таблиці функцій наведемо |
|
|||
|
програмний код |
|
|
|
|
|
AnsiString S = "Рядок символів"; |
|
|
||
|
int k=S.Pos(" "); |
// Віднаходження позиції пробілу; k=6 |
|
||
|
if(k != 0) |
|
|
|
|
|
{ S.Insert("чо",k-1); |
// Тепер рядок має вигляд "Рядочок символів" |
|
||
|
S.Delete(k+4,4); |
// Вилучення 4-х літер з позиції 10; |
|
||
} |
|
// тепер рядок має вигляд "Рядочок слів" |
|
||
|
S=S.UpperCase(); |
// S=" РЯДОЧОК СЛІВ" |
|
||
|
S=S.SubString(2,1)+S.SubString(9,3); |
// S="ЯСЛІ" |
|
||
|
S.SetLength(3); |
// S=" ЯСЛ" |
|
|
|
|
char *s; |
|
|
|
|
|
strcpy(s, S.c_str()); |
// Переведення рядка до типу char* |
|
||
Рядки AnsiString можна переприсвоювати один одному. Також їх можна “склеювати” за допомогою операції конкатенації +:
AnsiString S1="Hello", S2="world", S3;
S3=S1+" "+S2; |
// |
S3="Hello world" |
S3=S1; |
// |
S3="Hello" |
Рядки AnsiString можна порівнювати за допомогою операцій відношення (==, !=, <, <=, >, >=). Порівняння виконується згідно до ASCII-кодів символів рядка.
Символи і рядки |
257 |
До символів рядка AnsiString може бути застосовано функції опрацювання символів, розглянуті в попередньому підрозд. 7.1. Наприклад, у поданому далі фрагменті програми рядок S зчитується з Edit1, а потім обчислюється кількість великих літер kol у цьому рядку:
AnsiString S=Edit1->Text; int N=S.Length(), kol=0;
for(int i=1; i<=N; i++) if(isupper(S[i])) kol++;
Функції перетворення рядків AnsiStringна числа розглянуто в підрозд. 3.6.
Приклади програм з рядками AnsiString
Приклад 7.15 Увести рядок і віднайти у ньому всі слова, які розпочинаються з великої літери.
Текст програми:
void __fastcall TForm2::Button1Click
|
|
(TObject *Sender) |
{ Memo1->Clear(); |
|
|
AnsiString S, S1; |
|
|
S=Edit1->Text; |
// Введення рядка |
|
int p, n=S.Length(); // і обчислення його довжини |
||
// Долучення до рядка пробілу (ознакою завершення слова вважається пробіл) |
||
S=S+" "; |
|
|
p=1; |
// p – позиція першого символу чергового слова, для першого слова р=1 |
|
for(int i=1; i<=n; i++)
if(S[i]==' ') // Якщо символ є пробілом, це означає, що слово завершилось.
{// Якщо перший символ цього слова є великою літерою латиниці чи кирилиці, if(isupper(S[p]) || (S[p]>='А' && S[p]<='Я'))
{S1=S.SubString(p, i-p); // це слово копіюється до S1
Memo1->Lines->Add(S1); // і виводиться до Memo
}
// Тепер p – позиція початку нового слова (позиція пробілу плюс 1). p=i+1;
}
}
Приклад 7.16 Увести рядок і вилучити з нього всі слова, довжина яких є менше за три символи.
Текст програми
void __fastcall TForm1::Button1Click(TObject *Sender)
{AnsiString S=Edit1->Text; // Зчитування рядка
S=S+" "; // Приєднання пробілу, щоб “не втратити” останнє слово рядка int p=1, i, dl; // р – перша літера слова
//Оскільки довжина рядка буде змінюватися при вилучанні слів,
//то не можна в умові виходу із циклу ставити її заздалегідь обчислене значення for(i=1; i<=S.Length(); i++)
if(S[i]==' |
') // Якщо символ є пробілом, це є ознакою завершення слова |
{ dl=i-p; |
// Обчислення довжини слова |
