Копирование файла
Имея в своем распоряжении только функции GETCHAR и PUTCHAR вы можете, не зная ничего более об операциях вво- да-вывода, написать удивительное количество полезных прог- рамм. Простейшим примером может служить программа посимволь- ного копирования вводного файла в выводной. Общая схема име- ет вид: ввести символ WHILE (символ не является признаком конца файла) вывести только что прочитанный символ ввести новый символ
программа, написанная на языке "C", выглядит следующим обра- зом:
MAIN() /* COPY INPUT TO OUTPUT; 1ST VERSION */ { INT C;
C = GETCHAR(); WHILE (C != EOF) { PUTCHAR (C); C = GETCHAR(); } }
оператор отношения != означает "не равно". Основная проблема заключается в том, чтобы зафиксиро- вать конец файла ввода. Обычно, когда функция GETCHAR натал- кивается на конец файла ввода, она возвращает значение , не являющееся действительным символом; таким образом, программа может установить, что файл ввода исчерпан. Единственное ос- ложнение, являющееся значительным неудобством, заключается в существовании двух общеупотребительных соглашений о том, ка- кое значение фактически является признаком конца файла. Мы отсрочим решение этого вопроса, использовав символическое имя EOF для этого значения, каким бы оно ни было. На практи- ке EOF будет либо -1, либо 0, так что для правильной работы перед программой должно стоять собственно либо
#DEFINE EOF -1
либо
#DEFINE EOF 0
Использовав символическую константу EOF для представле- ния значения, возвращаемого функцией GETCHAR при выходе на конец файла, мы обеспечили, что только одна величина в прог- рамме зависит от конкретного численного значения. Мы также описали переменную 'C' как INT , а не CHAR , с тем чтобы она могла хранить значение, возвращаемое GETCHAR . как мы увидим в главе 2, эта величина действительно INT, так как она должна быть в состоянии в дополнение ко всем возмож- ным символам представлять и EOF. Программистом, имеющим опыт работы на "C", программа копирования была бы написана более сжато. В языке "C" любое присваивание, такое как
C = GETCHAR()
может быть использовано в выражении; его значение - просто значение, присваиваемое левой части. Если присваивание сим- вола переменной 'C' поместить внутрь проверочной части опе- ратора WHILE , то программа копирования файла запишется в виде:
MAIN() /* COPY INPUT TO OUTPUT; 2ND VERSION */ { INT C;
WHILE ((C = GETCHAR()) != EOF) PUTCHAR(C); }
Программа извлекает символ , присваивает его переменной 'C' и затем проверяет, не является ли этот символ признаком конца файла. Если нет - выполняется тело оператора WHILE, выводящее этот символ. Затем цикл WHILE повторяется. когда, наконец, будет достигнут конец файла ввода, оператор WHILE завершается, а вместе с ним заканчивается выполнение и функ- ции MAIN .
В этой версии централизуется ввод - в программе только одно обращение к функции GETCHAR - и ужимается программа. Вложение присваивания в проверяемое условие - это одно из тех мест языка "C", которое приводит к значительному сокра- щению программ. Однако, на этом пути можно увлечься и начать писать недоступные для понимания программы. Эту тенденцию мы будем пытаться сдерживать. Важно понять , что круглые скобки вокруг присваивания в условном выражении действительно необходимы. Старшинство операции != выше, чем операции присваивания =, а это означа- ет, что в отсутствие круглых скобок проверка условия != бу- дет выполнена до присваивания =. Таким образом, оператор
C = GETCHAR() != EOF
эквивалентен оператору
C = (GETCHAR() != EOF)
Это, вопреки нашему желанию, приведет к тому, что 'C' будет принимать значение 0 или 1 в зависимости от того, на- толкнется или нет GETCHAR на признак конца файла. Подробнее об этом будет сказано в главе 2/.