Тема 6 Масиви і вказівники

Лекція 21

Дані символьного типу

Рядки як символьні масиви

Ефективність мови С++ багато в чому визначається наявністю в ній розвинутих засобів для обробки символьної інформації. У стандартній бібліотеці С++ передбачено багато функцій, що виконують прості дії з символьними даними. Тому ця мова най­краще підходить для системної роботи: написання компіляторів, інтерпретаторів, операційних систем, редакторів тексту тощо.

У мові С та в ранніх версіях мови С++ рядки розглядалися як символьні масиви, і вся робота з ними ґрунтувалася на ви­користанні цих масивів. Розроблена бібліотека функцій string.h містить потужні засоби для роботи з рядковими масивами. Рядок являє собою масив символів, який закінчується нуль−символом. Нагадаємо, що нуль−символ має код, що дорівнює 0, і запис у вигляді керуючої послідовності ‘\0’. За розташуван­ням нуль−символа визначається фактична довжина рядка. Кіль­кість елементів символьного масиву складається з кількості сим­волів у рядку плюс 1, тому що нуль−символ також є елементом масиву.

Для опису рядка використовуються звичайні засоби опису масивів, наприклад: char str[25];. Індексування такого масиву, як і будь−якого іншого, починається з нуля.

Символьні послідовності, розділені тільки пропусками, роз­глядаються як один рядок.

Адреса першого символа рядка може використовуватися по− різному:

− якщо рядок застосовується при ініціюванні масиву типу char, адреса його першого елемента стає синонімом імені маси­ву. Наприклад, ідентичними є такі описи масиву:

char st [  ] = “Слово”;
char st [6] = “Слово”;
char st [6] = {‘С’ ‘л’ ‘о’ ‘в’ ‘о’ ‘\0’};

−  якщо рядок використовується для ініціювання покажчи­ка типу char*, адресапершого символа рядка буде початковим значенням покажчика, наприклад:
char *pst = “Слово”;.

Тут описується змінна−покажчик pst, яка одержує початко­ве значення, що дорівнює адресі першого елемента (симво­ла ‘С‘);

 якщо рядок використовується у виразі, що застосовує покажчик, то компілятор підставляє у вираз рядка адресу його першого символа.

Слід звернути увагу на те, що при описі символьного масиву його ім’я — не змінна, а покажчик−константа на початок рядка, тому її не можна використовувати в деяких операціях адресної арифметики. Зокрема, не можна здійснювати операцію присво­ювання вигляду:

char st [20];

st = “Петренко”; — запис неправильний, тому що не можна змінити значення st.

Виконання дій з елементами символьного масиву здійсню­ється через індекси або через покажчики. Для доступу до будь− якого символа рядка використовується індекс масиву char. Тоб­то, якщо описана змінна char str [3];, то третім елементом ма­сиву можна скористатися, записавши: str [2] або *(str+2).

У процесі роботи з елементами двовимірного масиву засто­совують або індекси масиву, або індекси покажчиків. Якщо описаний список прізвищ char spis [5] [15];, то для використан­ня символа масиву слід записати: spis [і][j] або *(spis [і] + j).

Аналогічно, якщо оголошений масив покажчиків char *str [5], що містить 5 елементів, кожний з яких вказує на рядок, то до­ступ до символа рядка можна здійснити з використанням запи­су *(str [і] + j).

 

Введення−виведення символьних масивів

 

Введення рядків можна здійснювати різними способами, найбільш розповсюдженими з яких є:

char st 15] = “Диск”; 

char st [  ] = “Диск”; 

char *pst = “Диск”;.

У цьому випадку двовимірні масиви можна ініціювати поріз­ному, наприклад, у вигяді:

char str [5][20] = {“Петренко И. И. “Головко С. С. “, . . . ,}; 

char str [ ][20] = {“Петренко И. И. “, “Головко С. С. . . . ,}; 

char *pst[5] = {“Петренко И. И. “, “Головко С. С. “, . . . ,}; 

char *pst[ ] = {“Петренко И. И. “, “Головко С. С. “…….., };

char st [5];  сіn >> st; 

char *pst;  сіn >> *pst; 

char str [5][20];  сіn >> str [i]; 

char *pst [5];  cin >> *(pst [i]);

get (st[i]);

  cin.get (str[i], size, endl);,

де size — кількість символів, що читаються;

  cin.getline (str[i], sizeof (str[i]−l));,

де sizeof() — функція визначення розміру рядка.

Виведення рядкових даних реалізується з використанням стандартного вихідного потоку cout:

cout << st;

cout.write(st, size); тощо.

Для потокового введеннявиведення доцільно застосовувати функції setw(w),setprecision(d), cout.width(w) і cout.precision(d).

Введення−виведення символьних масивів можна здійснити за допомогою відповідних функцій заголовного файла stdio.h., наприклад:

·         для введення рядківgets(st); та scanf (% s,st);

·         для виведення рядків — puts(st); і prin,tf(% s,st);.

 

 

Основні функції обробки символьних типів

 

У С рядки розглядалися як символьні масиви. Для роботи з ними розроблено бібліотеку функцій string.h, що містить ефективні засоби для роботи з рядками. Для обробки символьних типів даних бібліотека функцій string.h має велику кількість вбудованих функцій, що збіль­шують продуктивність праці програмістів та скорочують час на розробку програм, наприклад:

·         функції перевірки символів;

·         функції перетворення символів;

·         функції перевірки рядків;

·         функції маніпулювання рядками.

Функції наводяться у вигляді списків, що згруповані за їх розташуванням у заголовних файлах. Найчастіше надаються прототипи функцій, що описують, як слід використовувати функції у програмах.

Далі розглянемо прототипи, стислий опис, дію та методи­ку застосування основних функцій обробки даних символьно­го типу.

Функції копіювання рядків:

char strcpy (s, *st); — виконує операцію копіювання байтів рядка st у рядок s(включаючи  “\0”; повертає s);

 char *strdup (const char *str); — виконує копіювання рядка str і повертає покажчик на рядок−копію;

 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); — зміннаодержує значення, що дорівнює 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().

Приклад Скласти програму, яка вводить речення, здійснює розбиття його на слова, підраховує кількість символів у кожному сло­ві та виводить відповідну інформацію

// Використання функції 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  слово — Роби — містить 4 символів

2  слово — велике — містить 6 символів

3   слово — не — містить 2 символів

4   слово — обіцяючи — містить 8 символів

5   слово — великого — містить 8 символів

Функції перетворення рядків у числа та чисел у рядки знаходяться у файлі 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 до числа з плаваючою крапкою.