Ввод и вывод одного символа: функции getchar() и putchar().

Функция getchar() получает один символ, поступающий с пульта терминала (и поэтому имеющий название), и передает его выполняющейся в данный момент программе. Функция putchar() получает один символ, поступающий из программы, и пересылает его для вывода на экран. Ниже приводится пример очень простой программы. Единственное, что она делает, это принимает один символ с клавиатуры и выводит его на экран.

#include <stdio.h>

void main()

{

char ch;

ch = getchar();

putchar (ch);

}

Буферы.

При выполнении данной программы вводимый символ в одних вычислительных системах немедленно появляется на экране («эхо-печать»), в других же ничего не происходит до тех пор, пока вы не нажмете клавишу [ввод]. Первый случай относится к так называемому «небуферизованному» (прямому) вводу, означающему, что вводимый символ оказывается немедленно доступным ожидающей программе. Например, работа программы в системе, использующей буферизованный ввод, будет выглядеть следующим образом:

 

Вот длинная входная строка. [ввод]

В

 

В системе с небуферизованным вводом отображение на экране символа В произойдет сразу, как только вы нажмете соответствующую клавишу. Результат ввода-вывода при этом может выглядеть, например, так:

 

В Вот длинная входная строка.

 

Символ В, появившийся на второй позиции данной строки, — это непосредственный результат Зачем нужны буферы? Во-первых, оказывается, что передачу нескольких символов в виде одного блока можно осуществить гораздо быстрее, чем передавать их последовательно по одному. Во-вторых, если при вводе символов допущена ошибка, вы можете воспользоваться корректирующими средствами терминала, чтобы ее исправить. И когда в конце концов вы нажмете клавишу [ввод], будет произведена передача откорректированной строки.

Чтение данных.

Теперь возьмемся за что-нибудь несколько более сложное, чем чтение и вывод на печать одного символа — например, за вывод на печать групп символов. Желательно также, чтобы в любой момент можно было остановить работу программы; для этого спроектируем ее так, чтобы она прекращала работу при получении какого-нибудь специального символа, скажем *. Поставленную задачу можно решить, используя цикл while:

#include <stdio.h>

 

#define STOP '*'

 

void main(void)

 

{

 

char ch;

 

ch = getchar(); // строка 9

 

while (ch != STOP) { // строка 10

 

putchar(ch); // строка 11

 

ch = getchar(); //строка 12

 

}

}

При первом прохождении тела цикла функция putchar() получает значение своего apгумента в результате выполнения оператора, расположенного в строке 9; в дальнейшем, вплоть до завершения работы цикла, значением этого аргумента является символ, передаваемый программе функцией getchar(), расположенной в строке 12. Мы ввели новую операцию отношения !=, смысл которой выражается словами «не равно». В результате всего этого цикл while будет осуществлять чтение и печать символов до тех пор, пока не поступит признак STOP. Мы могли бы опустить в программе директиву #define и использовать лишь символ * в операторе while, но наш способ делает смысл данного знака более очевидным.

Чтение строки.

Одной из возможностей является использование символа «новая строка» (\n). Для этого нужно лишь переопределить признак STOP:

 

#define STOP '\n'

 

Какой это даст эффект? Очень большой: ведь символ «новая строка» пересылается при нажатии клавиши [ввод].

Чтение файла.

Каким может быть идеальный признак STOP? Это должен быть такой символ, который обычно не используется в тексте и, следовательно, не приводит к ситуации, когда он случайно встретится при вводе, и работа программы будет остановлена раньше, чем мы хотели бы.

Проблема подобного сорта не нова, и, к счастью для нас, она уже была успешно решена проектировщиками вычислительных систем. На самом деле задача, которую они рассматривали, была несколько отличной от нашей, но мы вполне можем воспользоваться их решением. Занимавшая их проблема касалась «файлов». Файлом можно назвать участок памяти, в который помещена некоторая информация. Обычно файл хранится в некоторой долговременной памяти, например, на гибких или жестких дисках, или на магнитной ленте. Чтобы отмечать, где кончается один файл и начинается другой, полезно иметь специальный символ, указывающий на конец файла. Это должен быть символ, который не может появиться где-нибудь в середине файла, точно так же как выше нам требовался символ, обычно не встречающийся во вводимом тексте. Решением указанной проблемы служит введение специального признака, называемого «End-of-File» (конец файла), или EOF, для краткости. Выбор конкретного признака EOF зависит от типа системы: он может состоять даже из нескольких символов. Но такой признак всегда существует, и компилятор с языка Си, которым вы пользуетесь, конечно же знает, как такой признак действует в вашей системе.

Каким образом можно воспользоваться символом EOF? Обычно его определение содержится в файле <stdio.h>. Общеупотребительным является определение:

 

#define EOF (- 1)

 

Это дает возможность использовать в программах выражения, подобные, например, такому:

while ((ch=getchar()) != EOF

#include <stdio.h>

void main(void)

{

int ch;

int count=0;

printf("Для окончания работы введите <CTRL/Z>\n");

while ((ch=getchar()) != EOF)

{

putchar(ch);

count++;

}

printf("\n Было введено %d символов.\n",count);

}

Отметим следующие моменты:

1. Нам не нужно самим определять признак EOF, поскольку заботу об этом берет на себя файл <stdio.h>.

2. Мы можем не интересоваться фактическим значением символа EOF, поскольку директива #define, имеющаяся в файле <stdio.h>, позволяет нам использовать его символическое представление.

3. Мы изменили тип переменной ch с char на int. Мы поступили так потому, что значениями переменных типа char являются целые числа без знака в диапазоне от 0 до 255, а признак EOF может иметь числовое значение -1. Эта величина недопустима для переменной типа char, но вполне подходит для переменной типа int. К счастью, функция getchar() фактически возвращает значение типа int, поэтому она в состоянии прочесть символ EOF.

4. Переменная ch целого типа никак не может повлиять на работу функции putchar(). Она просто выводит на печать символьный эквивалент значения аргумента.

5. При работе с данной программой, когда символы вводятся с клавиатуры, необходимо уметь вводить признак EOF. He думайте, что вы можете просто указать буквы E-O-F или число -1. (Число -1 служит эквивалентом кода ASCII данного символа, а не самим этим символом.) Вместо этого вам необходимо узнать, какое представление используется в вашей системе. В большинстве реализаций операционной системы UNIX, например, ввод знака [CTRL/D] (нажать на клавишу [D], держа нажатой клавишу [CTRL]) интерпретируется как признак EOF. Во многих микрокомпьютерах для той же цели используется знак [CTRL/Z].

54.Переключение и работа с файлами. Операционная система UNIX. Переключение вывода. Переключение ввода. Комбинированное переключение.

Переключение и работа с файлами.

Операция переключения — это средство ОС UNIX, а не самого языка Си. Но она оказалась настолько полезной, что при переносе компилятора с языка Си на другие вычислительные системы часто вместе с ним переносится в какой-то форме и эта операция. Более того, многие из вновь созданных операционных систем, таких, как MS-DOS 2, включают в себя данное средство. Поэтому, даже если вы не работаете в среде ОС UNIX существует большая вероятность того, что вы в той или иной форме сможете воспользоваться операцией переключения. Мы обсудим сначала возможности этой операции в ОС UNIX, а затем и в других системах.