СУЧАСНІ ІНФОРМАЦІЙНІ ТЕХНОЛОГІЇ / 3.Програмне забезпечення

 

К. т. н. Радельчук Г. І.

Хмельницький національний університет, Україна

ДЕЯКІ ОСОБЛИВОСТІ ОПРАЦЮВАННЯ СИМВОЛЬНИХ ДАНИХ У КОНСОЛЬНИХ СІ-ПРОГРАМАХ

 

Найкращий спосіб вивчення основ програмування – це складання консольних програм. Тому на початковому етапі навчання програмуванню студенти зазвичай створюють саме консольні програми, використовуючи мову програмування С.

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

Консольне вікно Windows є інтерфейсом, який надає операційна система для введення/виведення даних додаткам, які працюють у текстовому режимі, тобто таким, що не мають власного графічного інтерфейсу. Програми, які створюються на початковому етапі навчання, виконуються у консольному (командному) вікні. Вони використовують стандартні функції введення/ виведення мови С.

У багатьох програмах для виведення на екран часто використовуються рядки символів. Значенням символьного літерала є відповідне значення його коду (це, зрозуміло, не лише букви, цифри або спеціальні символи алфавіту С).

Деякі символи в символьних літералах записуються з допомогою ESC-послідовностей, наприклад, ‘\n’ (символ нового рядка).

Модель введення/виведення, що підтримується стандартною бібліотекою мови С, досить проста. Текстове введення/виведення, незалежно від того, звідки воно походить або куди направлено, розглядається як потік знаків. Текстовий потік – це послідовність знаків, розділених на рядки, де кожний рядок складається з нуля або більше символів з наступним знаком нового рядка. Це залишається відповідальністю бібліотеки – добитися того, щоб кожний потік введення/виведення відповідав цій моделі. C-програміст не повинен перейматись тим, як представлені рядки поза межами програми.

Стандартна бібліотека передбачає декілька функцій введення/виведення по одному знаку за раз, з яких функції getchar() і putchar() є найпростішими. Функція getchar(), кожний раз як її викликано, зчитує введений знак із текстового потоку та повертає цей знак як власне значення. Тобто, після виконання char ch; ch = getchar(); змінна ch міститиме знак введення (з клавіатури). 

Функція putchar() виводить один знак кожного разу як її викликано:

putchar(ch);

Ця функція виводить як знак вміст змінної ch; типово виведення надходить на екран.

Використання функцій getchar() і putchar() має низку особливостей, без врахування яких опрацювання текстових даних може бути некоректним.

Розглянемо, наприклад, такий код:

char a, b, c;

a = getchar(); putchar(a);

b = getchar(); putchar(b);

c = getchar(); putchar(c);

По ідеї після введення першого символу цей символ повинен відразу відображатися на екрані функцією putchar() і запрошуватися наступний символ, тому що далі йде знову виклик getchar().

У цій ситуації можливі два випадки.

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

2. Якщо ж при виконанні цього коду перед натисненням клавіші Enter ввести декілька символів (більше двох), то після натиснення Enter будуть виведені три перші символи введеного рядка, і програма завершиться. Хоча куди логічніше було б чекати, що буде прочитаний лише перший символ, потім виведений на екран і потім запитаний наступний символ.

Така дивна на перший погляд поведінка програми пов'язана не з мовою C, а з особливістю роботи операційних систем, в яких реалізовано буферизоване введення/виведення. При операціях введення/виведення виділяється область тимчасової пам'яті (буфер), куди і поміщаються символи, що поступають. Як тільки поступає спеціальний сигнал (наприклад, перехід на новий рядок при натисненні клавіші Enter), дані з буфера передаються по місцю свого призначення (зазвичай, на екран).

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

У першому випадку, коли був введений перший символ, то він був присвоєний змінній а і далі виведений в буфер. Натискування клавіші Enter – це сигнал для викиду даних із буфера, але це ще і символ переходу на новий рядок. Цей символ ‘\n’ і був записаний у змінну b, а другий введений користувачем символ – у змінну с.  Саме тому програма завершиться, не давши ввести третій символ.

Схематично це виглядає так.

Користувач вводить перший символ і натискує Enter;

a = getchar(); – зчитується перший символ і присвоюється змінній a;

putchar(a); – поміщає символ a в буфер;

Натисненням клавіші Enter вміст буфера, тобто символ a, “викидається” на екран і одночасно символ ‘\n присвоюється другій змінній b (b = getchar(););

putchar(b); – поміщає символ b в буфер, а оскільки значенням цього символа є ‘\n, то це є також сигналом вивести дані з буфера; результатом цього є переведення курсора на наступний рядок;

Користувач вводить другий символ, який присвоюється третій змінній с (c = getchar();) і виводиться в буфер (putchar(с);).

Після натискання клавіші Enter символ с виводиться з буфера на екран.

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

Щоб обійти цю ситуацію, можна ввести в програму тимчасову змінну (яка не буде використовуватись), у яку б перехоплювався символ ‘\n, наприклад, так:

char a, b, c, tmp;

a = getchar(); putchar(a);

tmp = getchar(); // “перехоплення символа ‘\n

putchar(tmp); // перехід на новий рядок

b = getchar(); putchar(b);

tmp = getchar(); // “перехоплення символа ‘\n

putchar(tmp); // перехід на новий рядок

c = getchar(); putchar(c);

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

Другий випадок  (коли до натиснення клавіші Enter вводиться кілька символів, тобто рядок) має іншу особливість. Коли користувач ввів перший символ, він (символ) попав у змінну а, далі спрацювала функція putchar(a) і символ попав в буфер. Оскільки Enter'а не було, то і вміст буфера на екрані не відображався. Користувач ввів другий символ, змінна b набула свого значення, а функція putchar(b) відправила це значення до буфера. Аналогічно з третім символом. Як тільки користувач натискає клавішу Enter, вміст буфера (повністю) виводиться на екран. Але символи, які були виведені на екран, виводяться не програмою, а операційною системою. Програма ж виводила символи в буфер ще до того, як користувач натиснув клавішу Enter.

Схематично це виглядає так.

Користувач послідовно вводить наприклад, три (чотири і більше) символи; далі, так само послідовно, в процесі введення:

a = getchar();зчитується перший символ і присвоюється змінній a;

putchar(a);поміщає символ a в буфер;

b = getchar();зчитується другий символ і присвоюється змінній b;

putchar(b);поміщає символ b в буфер;

c = getchar(); – зчитується третій символ і присвоюється змінній с;

putchar(с);поміщає символ с в буфер;

Всі ці операції відбуваються ДО натиснення користувачем клавіші Enter.

Всі інші введені символи прочитані не будуть; після натиснення клавіші Enter вміст буфера “викидається” на екран (буфер повністю очищається).

Така особливість дає можливість використовувати вказані функції для введення/виведення рядків (рисунок 1).

Рисунок 1 – Введення/виведення рядка символів

При спільному використанні функцій putchar() і getchar() можна користуватись коротшою формою запису:

while ((a = getchar()) != \n’) putchar(a);

Тут у змінній а завжди зберігається останній введений символ, але, перш ніж присвоїти їй нове значення, старе значення, за допомогою функції putchar(), скидається в буфер. Як тільки поступає символ нового рядка (користувач натиснув Enter), робота програми припиняється, а також відбувається виведення вмісту буфера на екран.

Якщо в умові циклу while буде не символ ‘\n’, а який-небудь інший, то програма продовжуватиме обробляти символи навіть після натиснення клавіші Enter. Внаслідок цього можна вводити і виводити багато рядків тексту.

В операційних системах і мовах програмування існує спеціальне значення, яке слугує ознакою закінчення потоку введення або ознакою кінця файла – константа EOF (End of File). При введенні символів з клавіатури цей знак означає “кінець списку введення”.

Наприклад:

int c;

while ((c = getchar()) != EOF)  putchar(c);

У операційній системі Windows можна передати функції getchar() значення EOF, якщо натискувати комбінацію клавіш Ctrl + Z; для операційних систем  GNU/Linux – Ctrl + D.

Не дивлячись на свою простоту (і навіть примітивність), функції getchar() і putchar() використовуються часто, оскільки посимвольный аналіз даних при введенні/виведенні є досить поширеним. Використовуючи функцію getchar(), можна, наприклад, отримати масив символів (рядок) і при цьому відсіяти непотрібні символи.

 

Література

1. Керниган Б. Язык программирования Си / Б. Керниган, Д. Ритчи. – М.: Вильямс, 2006. – 304 с.

2.  Денисов Ю. Текстовый ввод-вывод [Электронный ресурс] /  Ю. Денисов // НОУ ИНТУИТ. – Режим доступа:

http://www.intuit.ru/studies/courses/640/496/info/. – Название с экрана.