Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lektsii_OP / T10.doc
Скачиваний:
90
Добавлен:
17.03.2016
Размер:
586.75 Кб
Скачать

Введення-виведення рядків

Для введення-виведення рядків у мовах програмування, зазвичай, використовуються вбудовані підпрограми. Так у мові Pascal для цього служать стандартні процедури Read/ReadLn, Write/WriteLn. У мові С для введення-виведення рядків у консолі можуть використовуватися декілька альтернативних варіантів:

  • функції форматованого введення-виведення (scanf, printf);

  • спеціалізовані функції введення-виведення рядків (gets, puts).

Мова С++ розширює ці можливості шляхом використання потоків введення-виведення cin і cout.

Для використання спеціальних функцій та функцій форматованого введення-виведення рядків слід підключити заголовний файл <stdio.h>; для використання потокового введення-виведення - заголовний файл <iostream>.

Поточна довжина рядка, що вводиться, визначається автоматично. Зокрема, при введенні рядка з клавіатури його довжина визначатиметься як кількість символів, введених до натискання клавіші Enter, і символа кінця рядка (‘\0’), який автоматично заноситься після натиснення клавіші Enter. Наприклад,

char s[14], st[19], str[16];

cin>>s;

cout<<s;

scanf("%s", st); // ім'я рядка – покажчик

printf("Pядок: %s", st);

puts(s); //автоматичний перехід на новий рядок

У С/С++ введення рядків за допомогою функцій форматованого і операцій потокового введення-виведення має важливе обмеження: такі рядки вводяться тільки до першого роздільного символу (символа пробілу, табуляції або нового рядка), всі інші символи рядка - ігноруються. Тому ці засоби можна застосовувати тільки для введення окремих слів.

Для введення ж рядків-речень можна використати спеціалізовану функцію введення рядків gets. Формат її виклику є таким:

gets(рядок); // читання введеного з клавіатури рядка

При використанні функції gets, у випадку помилки введення, повертається нульовий покажчик NULL (описаний у бібліотеці stdio.h). Тому для перевірки правильності введення рядків доцільно ---використовувати цикл:

while (gets(s)!= NULL)

{ …

}

У С++ для введення рядків-речень можна також скористатися функцією getline, вбудованою у потік введення cin. Функція getline має два параметри: перший аргумент – рядок, який вводиться, другий – кількість символів. Наприклад,

char s[36];

cout<<"Enter row: ";

cin.getline( s, 30); // читання введеного з клавіатури рядка

Обробка рядків

Рядки можуть оброблятися як цілісний об’єкт, а також поелементно (посимвольно).

При посимвольній обробці доступ до конкретного символу рядка, як і до елементів масиву символів, здійснюється за індексом або за покажчиком (у С/С++). Можна вказати індекс рядка і поза його поточною довжиною. У цьому випадку зчитані символи будуть випадковими. При цьому присвоювання поза поточною довжиною не впливає на значення рядкової змінної.

При посимвольній обробці до окремого символа рядка можна застосовувати ті ж операції, що і до змінної символьного типу.

Рядки, як цілісні об’єкти, можна присвоювати, порівнювати і обробляти різноманітним чином, застосовуючи вбудовані засоби (підпрограми) обробки рядків, які, зазвичай, містяться у стандартних бібліотеках відповідної системи програмування. Тому для використання потрібної підпрограми відповідну бібліотеку (модуль – у Pascal, заголовний файл – у С/С++) слід підключити до основної програми. У С/С++ функції обробки C-рядків визначені у заголовному файлі string.h. Імена більшості із них починаються з префікса str.

Дії по обробці рядків різняться в залежності від мови програмування. Однак, усі їх можна згрупувати наступним чином:

  • ініціалізація рядка;

  • визначення довжини рядка;

  • копіювання рядка;

  • об'єднання (конкатенація) рядків;

  • порівняння рядків;

  • аналіз символів рядка;

  • пошук у рядку;

  • перетворення рядка.

Ініціалізація рядка. Ініціалізація рядків може здійснюватися при їх визначенні, а також шляхом явного присвоювання. При цьому вираз рядкового типу (літерал), зазвичай, можна присвоїти змінній-рядку. Однак, у мові С операція явного присвоювання літерала рядковій змінній заборонена. Тобто, якщо мають місце оголошення

char str[26];

char *st;

то оператор присвоєння виду

st = "адреса"

є допустимим, а оператор присвоєння

str = "помилка"

викличе повідомлення про помилку.

Розглянемо наступний фрагмент програми:

char str1[10]= "row 1";

char *str2;

str2=str1;

str2[0]='R';

cout <<str1<<endl; // row 1

Слід зауважити, що у даному випадку неправильно вважати, що в str2 міститься копія str1. Насправді цей покажчик вказує не на копію, а на той же самий рядок (str1). При будь-якій зміні вмісту str2 змінюється рядок str1.

Визначення довжини рядка. Визначити поточну довжину рядка можна тільки після його ініціалізації.

У Pascal для визначення поточної довжини рядка служить функція lenght, у С/С++ - функція strlen. Параметром даних функцій є рядок, значення довжини якого вони повертають. Прототип задання функції strlen:

size_t strlen(char *str),

де стандартний тип size_t визначено у заголовному файлі stddef.h як беззнаковий цілий. Наприклад,

char s[20];

cin >> s;

cout << “Length:" << strlen(s).

Слід зауважити, що значення довжини рядка, що повертає функція strlen, не включає нуль-символ (“\0”).

Відоме значення довжини рядка дозволяє обробляти символи рядка за допомогою циклу for.

Копіювання рядка. Мови програмування підтримують різні варіанти копіювання рядків. Так у Pascal можна тільки виділяти із рядка підрядок заданої довжини, починаючи із заданої позиції (функція Copy). У С/С++ передбачено більше таких можливостей. Зокрема, можна копіювати вміст одного рядка в інший повністю або частково (перші символи). Протопити відповідних функцій наведені у табл. 1.

Таблиця 1. Перелік основних функцій копіювання рядків у С/С++

Прототип функції

Опис

char* strcpy(char *str1, char *str2);

Копіює вміст рядка str2 у рядок str1

char* strncpy(char *str1, char *str2, size_t n);

Копіює n перших символів рядка str2 у рядок str1

Необхідно пам'ятати, що масив, який використовують для зберігання рядка str1, повинен бути достатньо великим, щоб в нього можна було помістити рядок з масиву str2. Інакше масив str1 переповниться, тобто відбудеться вихід за його межі, що може призвести до руйнування програми.

Наприклад,

char s1[10], s2[ ] = “example", s3[ ]="row";

strcpy(s1, s2);

cout <<s1<<endl; // example

strcnpy(s1, s3, 2);

cout <<s1<<endl; // roample

Слід зазначити, що саме функція strcpy використовується для ініціалізації рядка-змінної у С/С++.

Об'єднання рядків. Над рядковими змінними означена операція об'єднання (конкатенації), яка дозволяє дописати один рядок в кінець іншого. Під час об'єднання рядків поточна довжина рядка, до якого приєднується інший рядок, збільшується на довжину рядка, що додається. Збільшення довжини об'єднаного рядка здійснюється тільки у заданих під час оголошення межах.

У деяких мовах програмування об'єднання рядків здійснюється за допомогою вбудованих операцій (наприклад, операція конкатенації «+» у мові Pascal), у інших – за допомогою стандартних функцій. Так у табл. 2 наведено основні функції об'єднання рядків, передбачені у С/С++.

Таблиця 2. Перелік основних функцій об'єднання рядків у С/С++

Прототип функції

Опис

char *strcat(char *str1, char *str2);

Приєднує рядок str2 в кінець рядка str1

char *strncat(char *str1, char *str2, size_t n);

Приєднує n перших символів рядка str2 в кінець рядка str1

Наприклад,

char s1[30] ="example", s2[ ] =" of concatenation", s3[ ] =" rows";

strcat(s1, s2);

cout << s1 << endl; // example of concatenation

strcat(s1, s3, 4);

cout << s1 << endl; // example of concatenation row

Порівняння рядків. Для даних рядкових типів, зазвичай, визначено операції порівняння “менше“, “дорівнює“, “більше“. Принцип дії цих операцій базується на правилі порівняння даних символьного типу: з двох символів більшим вважається той, код якого має більше значення в кодовій таблиці символів (ASCII, UNICOD).

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

"рядок1" < "рядок2" .

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

"курс" < "курси".

У деяких мовах програмування (наприклад, у Pascal) для порівняння рядків означено операції порівняння “<“, “=“, “>“. У С/С++ різні варіанти порівняння рядків реалізуються за допомогою стандартних функцій (табл. 3).

Таблиця 3. Перелік основних функцій порівняння рядків у С/С++

Прототип функції

Опис

int strcmp(char *str1, char *str2);

Порівнює рядки str1 і str2

int stricmp(char *str1, char *str2);

Порівнює рядки str1 і str2 без урахування регістра

int strncmp(char *str1, char *str2, size_t n);

Порівнює перші n символів рядків str1 і str2

int strnicmp(char *str1, char *str2, size_t n);

Порівнює перші n символів рядків str1 і str2 без урахування регістра

Дані функції повертають значення 0, якщо рядки однакові (з урахуванням або без урахування регістра). Якщо рядок str1 лексикографічно (тобто відповідно до алфавітного порядку) більший від рядка str2, то повертається додатне число; якщо ж менший від рядка str2 - то повертається від’ємне число. Наприклад,

int r;

char s1[20], s2[10]= "example";

cin >>s1; // example

r = strcmp(s1, s2);

cout <<r <<endl; // 0

cin >>s1; // row

r = strcmp(s1, s2);

cout <<r <<endl; // 13

cin >>s1; // concatenation

r = strcmp(s1, s2);

cout <<r <<endl; // -2

Оскільки значення 0, що повертається даними функціями у випадку рівності порівнюваних рядків, відповідає логічному значенню false, то при перевірці рівності рядків рекомендується використовувати логічну операцію НЕ ("!"), щоб інвертувати умову (тобто змінити її на зворотню).Наприклад,

if (! strcmp(s1, s2))

cout << "Рядки однакові" << endl;

else

cout << "Рядки не однакові" << endl;

Аналіз символів рядка. Іноді у програмах виникає необхідність у перевірці приналежності симолів до певної класифікаційної групи: літера, цифра, знак пунктуації тощо. Для цього у С/С++ передбачено ряд стандартних функцій (табл. 4).

Таблиця 4. Перелік основних функцій С/С++ для аналізу символів

Прототип функції

Опис

int islower( int ch);

Перевіряє приналежність аргумента ch до рядкових літер

int isalnum( int ch);

Перевіряє приналежність аргумента ch до алфавітно-цифрових символів

int isalpha( int ch);

Перевіряє приналежність аргумента ch до літер

int isupper( int ch);

Перевіряє приналежність аргумента ch до прописних літер

int isdigit( int ch);

Перевіряє приналежність аргумента ch до цифрових символів

int ispunct( int ch);

Перевіряє приналежність аргумента ch до знаківпунктуації

int isspace( int ch);

Перевіряє, чи є аргумент ch пробілом

Якщо символ належить до відповідної класифікаційної групи, то відповідна повертає ненульове значення, інакше – 0. Наприклад,

for (int i= 1; i<= strlen(s1); i++)

if (isdigit(s1[i])) cout<<"Element "<<s[ i]<<" -digit"<<endl;

Пошук у рядку. Часто у програмах доводиться виконувати пошук у рядках окремих символів або підрядків. Деякі мови програмування мають незначні можливості виконання такого пошуку (наприклад, у Pascal для цього служить лише функція Pos, яка визначає позицію першої появи заданого підрядка у вказаному рядку), інші – значно більші. Так у С/С++ передбачені наступні можливості пошуку у рядках:

  • пошук символів;

  • пошук підрядків;

  • пошук лексем.

Пошук окремих символів підтримується функціями, наведеними у табл. 5.

Таблиця 5. Перелік основних функцій С/С++ для пошуку символів у рядках

Прототип функції

Опис

char *strchr(char *str, int ch);

Шукає перше входження символа сh у рядок str

char *strrchr(char *str, int ch);

Шукає останнє входження символа сh у рядок str

char *strpbrk(char *str1, char *str2);

Шукає першевходження будь-якого символу з рядка str2 у рядок str1

Наприклад,

if (strchr(filename, '.'))

cout << "Ім'я вже містить розширення" << endl;

else strcat(filename, ".TXT");

Вираз strchr( filename , ' .' ) повертає покажчик на символ точки в рядку, що адресується покажчиком filename. Якщо такий символ не знайдений, функція strchr повертає NULL. Оскільки ненульові значення означають "істину", то можна використовувати функцію strchr як таку, що повертає значення "істина" / "хибність".

Також можна застосувати функцію strchr для присвоювання покажчика на підрядок, що починається з заданого символу. Наприклад, якщо має місце оголошення

char *р,

і покажчик filename адресує рядок TEST.ТХТ, то результатом дії оператора

p = strchr ( filename , ' . ')

є встановлення покажчика р на символ ' .' рядка filename (рис. 1).

Рис. 1. Адресація знайденого у рядку символа

Якщо має місце опис

char *ptr, *p;

char str[20]= "this is a test ";

то оператор

ptr = strrchr(str, ‘s’);

встановить покажчик ptr рівним адресі підрядка "st" наприкінці рядка "this is a test", а оператор

p = strpbrk(str,"ae");

встановить покажчик p рівним адресі підрядка "a test" рядка "this is a test".

Окрім пошуку символів в рядку, можна також організувати пошук і підрядків вказаного рядка. Для цього ц С/С++ служать функції, наведені у табл. 6.

Таблиця 6. Перелік основних функцій С/С++ для пошуку підрядків у рядках

Прототип функції

Опис

char *strstr(char *str1, char *str2);

Шукає перше входження підрядка str2 у рядок str1

size_t strcspn(char *str1, char *str2);

Визначає довжину початкового інтервалу рядка str1, що не містить символів з рядка str2; тобто, повертає індекс першого символу рядка str1, який збігається з будь-яким із символів в рядку str2

size_t strspn(char *str1, char *str2);

Визначає довжину початкового інтервалу рядка str1, що містить символи з рядка str2

Наприклад,

p = strstr(filename, ".txt");

if (p) cout << " Ім'я вже містить потрібне розширення" << endl;

else

{ p = strchr(filename, '.');

if (p) *p= NULL; //видалити будь-яке інше розширення

strcat(filename, ".txt");

}

Цей фрагмент програми створює ім'я файлу, яке обов'язково закінчується розширенням .txt. Щоб визначити, чи є в імені файлу це розширення, програма виконує оператор

p = strstr( filename, ".txt" ) ;

Подібно strchr, функція strstr повертає адресу підрядка або NULL, якщо шуканий рядок не знайдений. Якщо заданий підрядок знайдений​​, покажчик p стане рівним його адресі, в даному прикладі - адресі точки (".") в підрядку ". txt".

Даний приклад також демонструє спосіб відсікання рядка в позиції заданого символа або підрядка. Якщо при виклику функції strstr результат пошуку першої точки в рядку filename не нульовий, то виконається оператор, який запише замість точки нуль-символ:

*p = NULL ; //або *р=’\0’

Тим самим буде приєднано новий кінець рядка в тому місці, де раніше знаходилося розширення файлу. Тепер рядок готовий до додавання нового розширення шляхом виклику функції strcat.

Наступний фрагмент показує приклади використання функцій strspn і strcspn:

len1 = strcspn("this is a test","is");

cout << len1 << endl; // 2

len2 = strspn("this is a test","this ");

cout << len2 << endl; // 8

Іноді виникає необхідність у знаходженні компонент рядків (лексем). Якщо складові частини рядка відокремлені одна від одної комами, пробілами або іншими роздільниками, то можна використати функцію strtok для розкладання рядка на кілька підрядків. Протопип даної функції:

char *strtok(char *str1, char *str2);

Ця функція дозволяє перетворити вихідний рядок (str1) в послідовність лексем, розділених символами, що містяться у рядку роздільників str2. Якщо лексема знайдена, то повертається покажчик на неї, інакше – NULL. Якщо у рядку декілька лексем, то дана функція викликається повторно: при першому виклику їй передається адреса вихідного рядка (str1), потім – NULL. Пошук продовжується доти, доки strtok не поверне NULL. Наприклад,

char str[ ] ="f1.txt, f2.pas f3.cpp“, *lim = ", ";

char *ptr = strtok(str, lim);

while (ptr!= NULL)

{ puts(ptr);

ptr = strtok(NULL, lim);

}

Після першого виклику функція strtok повертає адресу першої лексеми, відокремленої від наступних заданим роздільником: в даному прикладі - комою. Одночасно функція strtok вставляє нуль-символ ('\0' ) в позицію першого заданого обмежувача. Тим самим створюється підрядок, адреса якого повертається функцією strtok за допомогою покажчика ptr. Якщо задані роздільники не виявлені, функція strtok повертає NULL.

Окрім того, ця функція налаштовує свій внутрішній покажчик на символ, наступний за кінцем останнього сформованого підрядка, щоб наступний виклик strtok міг продовжити розкладання рядка. Для цього слід передати як перший аргумент значення NULL - це послужить сигналом для функції strtok використовувати її внутрішній покажчик як стартову адресу для пошуку іншого роздільника. Таким чином, щоб виділити інші компоненти рядка, потрібно просто викликати функцію strtok кілька разів, і кожного разу першим її аргументом має бути NULL.

Перетворення рядків. Мови програмування підтримують різні варіанти перетворення рядків. Так у Pascal можна видаляти заданий підрядок із рядка або вставляти його у рядок, перетворювати малі літери у прописні, а також перетворювати числові значення в рядкові і навпаки.

У С/С++ можливості перетворення рядків значно ширші. Перелік основних із них наведений у табл. 7.

Таблиця 7. Перелік основних функції перетворення рядків у С/С++

Прототип функції

Опис

char *strlwr(char *str);

Приводить символи рядка до нижнього регістра

char *strupr(char *str);

Приводить символи рядка до верхнього регістра

char *strset(char *str, int ch);

Замінює всі символи рядка на символ сh

char *strnset(char *str, int ch, size_t n);

Замінює перші n символів у рядку на символ сh

char *strrev(char *str);

Інвертує рядок

Наприклад,

strcpy(str, "this is a test");

strupr(str);

cout << str << endl; // THIS IS A TEST

strnset(str, “-“, 9);

cout << str << endl; // --------- TEST

strrev(str);

cout << str << endl; // TSET ---------

До функцій перетворення рядків також відносять функції, які дозволяють здійснювати перетворення рядка у відповідне числове значення. Основні такі функції мови С/С++ наведені у табл. 8.

Таблиця 8. Перелік основних функції перетворення рядків і чисел у С/С++

Прототип функції

Опис

double atof(const char *str);

Перетворює рядок str в число з плаваючою крапкою типу double; якщо рядок str не є коректним текстовим поданням числа double, то повертається NULL (заголовний файл - math.h)

double strtod ( const char *str, char **endptr );

Перетворює рядок str в значення типу double; параметр endptr встановлюється так, щоб вказувати на "невикористаний" залишок початкового рядка, якщо такий існує (заголовний файл - stdlib.h)

int atoi(const char *str);

Перетворює рядок str в число типуint (заголовний файл - stdlib.h)

long atol(const char *str);

Перетворює рядок str в число типуlong (заголовний файл - stdlib.h)

Наприклад,

char str []= “10.4e-1”, *ptr;

double d = atof(str);

cout << d <<endl; // 1.04

double d = strtod(str, &ptr);

cout << d <<endl; // 1.04

int n = atoi(str);

cout << n <<endl; // 10

Після числа у рядку перетворення може слідувати будь-який символ, який не може бути частиною допустимого числа з плаваючою крапкою - пробіли, символи табуляції і порожнього рядка, розділові знаки (але не точки) і символи, відмінні від літер "Е" або "е". Це означає, що, якщо функція atof або функція strtod викликаються з аргументом "10.4е-1 HELLO", то все рівно буде повернуто значення 1.04. Різниця між цими функціями полягає у тому, що функція strtod має параметр-покажчик (endptr), який встановлюється так, щоб вказувати на "невикористаний" залишок початкового рядка, якщо такий існує. Це означає, що для попередгього прикладу функція strtod поверне значення 1.04, а параметр-покажчик endptr буде вказувати на пробіл, який передує слову "HELLO". Наприклад, наступна програма читає числа з плаваючою точкою з текстового рядка:

#include <stdlib.h>

#include <ctype.h>

#include <stdio.h>

int main(void)

{ char *end, *start = "100.00 notebooks 200.00 pencils";

end = start;

while(*start)

{ printf("%f, ", strtod(start, &end));

printf("Rest of the line: %s\n", end);

start = end;

while(!isdigit(*start) && *start) start++; // пропускає символи, що не входять в числа

}

system("pause");

}

Ось що виводить ця програма:

Мови програмування також підтримують функції перетворення числових значень в рядкові. Основні такі функції мови С/С++ наведені у табл. 9.

Таблиця 9. Перелік основних функції перетворення рядків і чисел у С/С++

Прототип функції

Опис

char *itoa(int value, char *str, int radix);

Перетворює значення цілого типу value в рядок str з урахуванням зазначеної системи числення radix

char *gcvt(double value, int ndig, char *buf);

Перетворює число з плаваючою крапкою value типу double в рядок довжиною ndig цифр, який адресується аргументом buf. Спосіб подання вибирається функцією (звичайний або експонентний).

char *ecvt(double value, int ndig, int *dec, int *sign);

Перетворює число з плаваючою точкою value типу double в символьний рядок довжиною ndig цифр і повертає адресний покажчик отриманого рядка.

Якщо кількість цифр в value перевищує ndig, остання значуща цифра округлюється, якщо цифр менше, ніж ndig, - рядок доповнюється нулями.

Результуючий рядок не містить символу десяткової точки - позиція десяткової точки щодо початку рядка непрямим чином зберігається в параметрі dec (від'ємне значення dec означає, що десяткова точка розміщена лівіше першої цифри на dec позицій, додатне – правіше першої цифри на dec позицій).

Аргумент sign вказує на ціле, що визначає знак перетворюваного числа (якщо sign дорівнює 0, то число додатне, інакше - від'ємне).

Наприклад,

char result[17];

int value=23677;

itoa(value, result, 10);

cout << "10:" << result << endl; // 23677

itoa(value, result, 16);

cout << "16:" << result << endl; // 5c7d

itoa(value, result, 2);

cout << "2:" << result << endl; // 101110001111101

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

int dec, sign;

char *buffer;

int precision=10;

buffer= ecvt(314.15926535, precision, &dec, &sign);

cout << buffer << endl; // рядок -"3141592654", dec=3, sign=0

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

char buffer[50];

int precision = 7;

gcvt (-3.1415e3, precision, buffer);

cout << buffer << endl; // -3141.5

Соседние файлы в папке lektsii_OP