Файловый ввод-вывод 539
Функция getc() возвращает специальное значение EOF, если она пытается прочитать символ и обнаруживает, что достигнут конец файла. Таким образом, программа С выясняет, что она достигла конца файла, только после попытки чтения за концом файла. (Это не похоже на поведение в ряде других языков, в которых предусмотрена специальная функция для проверки на предмет конца файла перед попыткой чтения.)
Чтобы избежать проблем с попыткой чтения пустого файла, при файловом вводе должен применяться цикл с входным условием (не цикл do while). Из-за конструктивных особенностей getc() (и других функций ввода С) программа должна выполнять чтение до входа в тело цикла. Тогда показанное ниже проектное решение вполне по дойдет:
// правильное проектное решение #1
int ch; // переменная int для хранения EOF
FILE * fp;
fp = fopen("wacky.txt", "r");
ch = getc(fp); // получить первоначальный ввод
while (ch ! = EOF)
{
putchar(ch); // обработать ввод
ch = getc(fp); // получить следующий ввод
}
Это решение можно ужать следующим образом:
// правильное проектное решение #2 int ch;
FILE * fp;
fp = fopen("wacky.txt", "r"); while ((ch = getc(fp)) ! = EOF)
{
putchar(ch); // обработать ввод
}
Поскольку оператор ввода является частью проверочного условия while, он выполняется до того, как поток управления войдет в тело цикла.
Проектных решений вроде приведенного ниже вы должны избегать:
// неудачное проектное решение (две проблемы) int ch;
FILE * fp;
fp = fopen("wacky.txt", "r");
while (ch != EOF) // первым используется неопределенное значение ch {
ch = getc(fp); // получить ввод
putchar(ch); // обработать ввод
}
Первая проблема связана с тем, что когда переменная ch в первый раз сравнивается с EOF, ей еще не было присвоено значение. Вторая проблема в том, что если getc() возвращает EOF, то цикл пытается обработать EOF, как если бы это был допустимый символ. Указанные дефекты поддаются исправлению. К примеру, вы могли бы инициализировать ch каким-то фиктивным значением и поместить внутрь цикла оператор if, но зачем об этом беспокоиться, если доступны правильные проектные решения?
Указанные меры предосторожности касаются и других функций ввода. Они также возвращают сигнал об ошибке (EOF или пустой указатель), столкнувшись с концом файла.
540 Глава 13
ФУНКЦИЯ fclose()
Функция fclose(fp) закрывает файл, идентифицируемый fp, при необходимости сбрасывая буферы. В более ответственной программе вы должны удостовериться, что файл закрыт успешно. Функция fclose() возвращает значение 0, если файл был закрыт успешно, и EOF, если нет:
if (fclose(fp) != 0)
printf("Ошибка при закрытии файла %s\n", argv[1]);
Функция fclose() может завершиться неудачно, если, например, жесткий диск заполнен, съемное устройство храпения извлечено или произошла ошибка ввода-вывода.
Указатели на стандартные файлы
В stdio.h три указателя файлов ассоциированы с тремя стандартными файлами, которые автоматически открываются программами на С.
Стандартный файл Указатель файла Обычное устройство
Стандартный ввод stdin Клавиатура
Стандартный вывод stdout Экран
Стандартный вывод ошибок stderr Экран
Все они имеют тип указателя на FILE, поэтому могут использоваться в качестве аргументов для стандартных функций ввода-вывода подобно fp в приведенном ранее примере. Давайте теперь перейдем к рассмотрению примера, в котором создается новый файл и в него производится запись.
Бесхитростная программа уплотнения файла
Следующая программа копирует избранные данные из одного файла в другой. Она открывает два файла одновременно с применением режима "г" для одного и режима "w" для второго. Программа (показанная в листинге 13.2) уплотняет содержимое первого файла, грубо оставляя только каждый третий символ. В итоге она помещает уплотненный текст во второй файл. Имя второго файла образуется путем дополнения старого имени расширением . red. Использование аргументов командной строки, открытие одновременно более одного файла и добавление расширения к имени файла в общем случае являются довольно практичными приемами. Эта конкретная форма уплотнения файла имеет ограниченное применение, по, как вы увидите, такие случаи возникают. (Программу несложно модифицировать, чтобы для предоставления имен файлов вместо аргументов командной строки использовались стандартные методы ввода-вывода.)
Листинг 13.2. Программа reducto.с

Файловый ввод-вывод 541

Читать дальше