Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

352

Программирование на языке Си

ся во входном потоке. Если функция scanf() применяется для организации диалога с пользователем, то, будучи активизирова­ на повторно, она дочитает из входного потока "остатки" преды­ дущей порции данных, а не начнет анализ новой порции, введенной пользователем.

Предположим, что программа запрашивает у пользователя номер дома, в котором он живет. Программа обрабатывает код возврата функции scanf() в предположении, что во входных данных присутствует одно поле.

#±nclude <stdio.h> int main ()

{

int number;

printf("Введите номер дома: ") ; while (scanf("%d", (number) != 1)

printf("Ошибка. Введите номер дома: "); printf("Номер вашего дома %d\n", number); return 0;

}

При правильном вводе данных (введено целое десятичное число) результат будет следующий:

Введите номер дома: 25 ■Номер вашего дома 25

Предположим, что пользователь ошибся и ввел, например, следующую последовательность символов "@%". (Эти символы будут введены, если нажаты клавиши '2' и '5', но на верхнем ре­ гистре, т.е. одновременно была нажата клавиша <Shifit>.) В этом случае получится следующий результат:

Введите номер дома: 0% Ошибка. Введите номер дома: Ошибка. Введите номер дома:

Ошибочный символ @ прерывает работу функции scanf( )> но сам остается во входном потоке, и вновь делается попытка его преобразования при повторном вызове функции в теле цик­ ла while. Однако эта и последующие попытки ввода оказывают-

Глава 7. Ввод и вывод

353

ся безуспешными, и программа переходит к выполнению беско­ нечного цикла.' Необходимо до предоставления пользователю новой попытки ввода номера дома убрать ненужные символы из входного потока. Это предусмотрено в следующем варианте той же программы:

#include <stdio.h> int main()

{

int. number;

printf("Введите номер дома; "); while (scanf("%d", (number) != 1)

{

/* Ошибка. Очистить входной поток: */ while (getchar() •= J\n')

/. T

printf("Ошибка. Введите номер дома: ");

)

printf("Номер вашего Дома %d\n", number); return 0;

>

Теперь при неправильном вводе данных результат будет та­ ким:

Введите номер дома: 0% . Ошибка. Введите номер дома: 25 Номер вашего дома 25

7.1.3. Работа с файлами на диске

Аналогичным образом (так же как это делается при работе со стандартными потоками ввода-вывода stdin и stdout) можно осуществлять работу с файлами на диске. Для этой цели в биб­ лиотеку языка Си включены следующие функции:

fgetc(), getc() - ввод (чтение) одного символа из файла; fputc(), putc() - запись одного символа в файл; fprintf() - форматированный вывод в файл;

fscanf() - форматированный ввод (чтение) из файла;

23~3124

354

Программирование на языке Си

fgets() -

ввод (чтение) строки из файла;

fputs() - запись строки в файл.

Различие между функциями fgetc(), getc() и fputc(), putc() здесь не рассматривается, и поэтому в примерах мы будем ис­ пользовать только одну из них.

Двоичный (бинарный) режим обмена с файлами. Двоич­ ный режим обмена организуется с помощью функций getc( ) и putc(), обращение к которым имеет следующий формат:

с = getc(fp); putc(c, fp);

где

fp - указатель на поток;

 

с - переменная типа int для приема очередного символа

из файла или для записи ее значения в файл.

Прототипы функции:

int getc ( FILE *stream );

int

putc ( int c, FILE *stream );

В качестве примера использования функций getc( ) и putc() рассмотрим программы ввода данных в файл с клавиатуры и программу вывода их на экран дисплея из файла.

Программа ввода читает символы с клавиатуры и записывает их в файл. Пусть признаком завершения ввода служит посту­ пивший от клавиатуры символ Имя файла запрашивается у пользователя. Если при вводе последовательности символов была нажата клавиша <Enter>, служащая разделителем строк при вводе с клавиатуры, то в файл записываются управляющие коды "Возврат каретки" (CR - значение 13) и "Перевод строки" (LF - значение 10). Код CR в дальнейшем при выводе вызывает перевод маркера (курсора) в начало строки экрана дисплея. Код LF служит для перевода маркера на новую строку дисплея. Зна­ чения этих кодов в тексте программы обозначены соответствен­ но идентификаторами CR и LF, т.е. CR и LF - именованные константы. Запись управляющих кодов CR и LF в файл позволя­ ет при последующем выводе файла на экран отделить строки друг от друга.

Глава 7. Ввод и вывод

355

В приводимых ниже программах используются уже рассмот­ ренные выше функции getchar(), putchar() для посимвольного обмена со стандартными потоками stdin, stdout.

/* Программа ввода символов */ #include <stdio.h>

int main()

{

FILE *fp; /* Указатель на поток */ char с ;

/* Восьмеричный код "Возврат каретки": */ const char CR='\015';

/* Восьмеричный код "Перевод строки": */ const char LF = ’\012’;

char fname[20]; /* Массив для имени файла */ /* Запрос имени файла: */

puts("введите имя файла: \п"); gets(fname);

/* Открыть файл для записи: */

if ((fp = fopen(fname,"w")) = NULL)

{

perror(fname); return 1;

>

/* Цикл ввода и записи в файл символов: */ while ((с = getchar()) •= '#')

{

if (с == '\п')

I (

putc(CR, fp); putc(LF, fp);

>

else putc(c, fp);

)

/* Цикл ввода завершен; закрыть файл: */ fclose(fp);

return 0;

)

Следующая программа читает поток символов из ранее соз­ данного файла и выводит его на экран дисплея:

23*

356

Программирование на языке Си

/* Программа вывода символьного файла на экран дисплея */

♦include <stdio.h> int main ()

{

FILE *fp; /* Указатель на поток */ char с ;

char fname[20]; /* Массив для имени файла */

/* Запрос имени файла: */ puts("введите имя файла: \п "); gets(fname);

/* Открыть файл для чтения: */

if ((fp = fopen(fname,"r")) = = NULL)

{

perror(fname); return 1;

)

/* Цикл чтения из файла и вывода символов на экран: */

while ((с = getc(fp)) != EOF) putchar(c);

fclose(fp); /* Закрыть файл */ return 0;

>

Программу чтения символов из файла можно усовершенст­ вовать, добавив возможность вывода информации на экран пор­ циями (кадрами):

/* Усовершенствованная программа вывода символьного файла на экран дисплея по кадрам */

♦include <stdio.h> int main ()

{

FILE *fp; /* Указатель на поток) */ char с;

/* Восьмеричный код "Перевод строки": */ const char ЕЕ='\012';

int МАХ=10; /* Размер кадра */ int nline; /* Счетчик строк */

char fname[10]; /* Массив для имени файла */

Глава 7. Ввод и вывод

357

/* Запрос имени файла: */ puts("введите имя файла \п"); gets(fname);

/* Открыть файл для чтения: */

if ((£р = fopen(fname,"г")) = = NULL)

{

perror(fname); return 1;

}

while (1)

{ /* Цикл вывода на экран */ nline = 1;

while (nline<MAX) /* Цикл вывода кадра */

{

с = getc(fp); if (с = EOF)

{

fclose(fp); return 0;

>

if (c == LF) nline++; putchar(c);

} /* Конец цикла вывода кадра*/ getchar(); /* Задержка вывода до

нажатия любой клавиши */ ) /* Конец цикла вывода */

} /* Конец программы */

В этой программе после вывода очередного кадра из МАХ строк для перехода к следующему кадру необходимо нажать любую клавишу.

Используя двоичный обмен с файлами, можно сохранять на диске информацию, которую нельзя непосредственно вывести на экран дисплея (целые и вещественные числа во внутреннем представлении).

Необходимые примеры и разъяснения приводятся в главе 8 при описании программы обслуживания базы данных о сотруд­ никах предприятия. Функции save() и load( ) из этого примера (см. §8.2) позволяют сохранить во внешней памяти и загрузить из внешней памяти базу данных о сотрудниках предприятия.

358

Программирование на языке Си

Строковый обмен с файлами. Для работы с текстовыми файлами, кроме перечисленных выше, удобно использовать функции fgets() и fputs(). Прототипы этих функций в файле stdio.h имеют следующий вид:

int fputs(const char *s, FILE *stream); char * fgets(char *s, int n, FILE *stream);

Функция fputs() записывает ограниченную символом '\0' строку (на которую указывает s) в файл, определенный указате­ лем stream на поток, и возвращает неотрицательное целое. При ошибках функция возвращает EOF. Символ '\0' в файл не пере­ носится, и символ '\п' не записывается в конце строки вместо '\0'..

Функция fgets() читает из определенного указателем stream файла не более (п-1) символов и записывает их в строку, на ко­ торую указывает s. Функция прекращает чтение, как только про­ чтет (п-1) символов или встретит символ новой строки '\п', ко­ торый переносится в строку s. Дополнительно в конец каждой строки записывается признак окончания строки '\0'. В случае успешного завершения функция возвращает указатель s. При ошибке или при достижении конца файла, при условии, что из файла не прочитан ни один символ, возвращается значение NULL. В этом случае содержимое массива, который адресуется указателем s, остается без изменений. Напомним, что в отличие от fgets() функция gets() отбрасывает символ '\п'.

Проиллюстрируем возможности указанных функций на про­ грамме, которая переписывает текст из одного файла в другой. На этом же примере еще раз проиллюстрируем особенность языка Си - возможность передачи информации в выполняемую программу из командной строки (см. §5.8).

В стандарте языка Си определено, что любая программа на языке Си при запуске на выполнение получает от операционной системы информацию в виде значений двух аргументов - argc (сокращение от argument count - счетчик аргументов) и argv (сокращение от argument vector). Первый из них определяет ко­ личество строк, передаваемых-в виде массива указателей argv. По принятому соглашению argv[0] это всегда указатель на стро­

Глава 7. Ввод и вывод

359

ку, содержащую полное имя файла с выполняемой программой. Остальные элементы массива argv[l],...,argv[argc-l] содержат ссылки на параметры, записанные через пробелы в командной строке после имени программы. (Третий параметр char * envp[ ] мы уже рассмотрели в §5.8, но он используется весьма редко.)

Предположим, что имя выполняемой программы copyfile.exe и мы хотим с ее помощью переписать содержимое файла fl .dat в файл f2.txt. Вызов программы из командной строки имеет вид:

>copyfile.exe f1 .dat f2.txt

Текст программы может быть таким:

#include <stdio.h>

main(int argc, char *argv[ ])

{

char cc[256];/* Массив для обмена с файлами */ FILE *fl, *f2; /* Указатели на потоки */

if (argc != 3) /* Проверка командной строки */

{

printf("\n Формат вызова программы: ") ; printf("\n copyfile.exe"

printf("\n файл-источник файл-приемник"); return 1;

}

if ((fl = fopen(argv[l], "r")) == NULL) /* Открытие входного файла */

{

perror(argv[1]); return 1;

>

if ((f2 = fopen (argv [2] , "w")) == NULL) /* Открытие выходного файла */

{

perror(argv[2]); return 1;

>

while (fgets(cc, 256, fl) != NULL) fputs(cc, f2);

fclose(f1);

fclose(f2); return 0;

}

360

Программирование на языке Си

Обратите внимание, что значение argc явно не задается, а оп­ ределяется автоматически. Так как argv[0] определено всегда, то значение argc не может быть меньше 1. При отсутствии в ко­ мандной строке двух аргументов (имен файлов ввода-вывода) значение argc оказывается не равным трем, и поэтому на экран выводится поясняющее сообщение:

Формат вызова

программы:

файл-приемник

copyfile.exe '

файл-источник

Если имя входного файла указано неудачно или нет места на диске для создания выходного файла, то выдаются соответст­ вующие сообщения:

Ошибка при открытии файла f1.dat

или

Ошибка при открытии файла f 2 .txt

Режим форматного, обмена с файлами. В некоторых случа­ ях информацию удобно записывать в файл в виде, пригодном для непосредственного (без преобразования) отображения на экран дисплея, т.е. в символьном виде. Например, для сохране­ ния результатов работы программы, чтобы затем распечатать их на бумагу (получить "твердую" копию) или когда необходимо провести трассировку программы - вывести значения некото­ рых переменных во время выполнения программы для их по­ следующего анализа. В этом случае можно воспользоваться функциями форматного ввода (вывода) в файл fprintf() и fscanf(), которые имеют следующие прототипы:

int fprintf ( указатель напоток, форматная-строка, список-переменных)',

int fscanf ( указатель на поток, форматная строка, списокадресов переменных)',

Как и в функциях printf() и scanf(), форматная строка мо­ жет быть определена вне вызова функции1, а в качестве фактиче­ ского параметра в этом случае будет указатель на нее.

Глава 7. Ввод и вывод

361

От рассмотренных выше функций p rin ts ), scanf() для фор­ матного обмена с дисплеем и клавиатурой функции fprintf() и fscanf() отличаются лишь тем, что в качестве первого парамет­ ра в них необходимо задавать указатель на поток, с которым производится обмен. В качестве примера приведем программу, создающую файл int.dat и записывающую в него символьные изображения чисел от 1до 10 и их квадратов [9]:

#include <stdio.h> int main()

{

FILE *fp; /* Указатель на поток */

int п;

fopen("int.dat","w")) = NULL)

if ((fp =

{

 

 

perror("int.dat");

 

return

1;

-

> .

'

for (n=l;

n<ll; n++)

n, n*n);

fprintf(fp, "%d %d\n",

fclose(fp);

 

return 0;

 

 

}

В результате работы этой программы в файле int.dat форми­ руется точно такая же "картинка", как и в том случае, если бы мы воспользовались вместо функции fprintf() функцией printf() для вывода результатов непосредственно на экран дис­ плея. В этом легко убедиться, воспользовавшись соответствую­ щей обслуживающей программой операционной системы для вывода файла int.dat на экран дисплея. Несложно и самим напи­ сать такую программу, используя для форматного чтения дан­ ных из файла функцию fscanf():

#include <stdio.h> int main ()

{

FILE *fp; /* Указатель на .поток */ int n, nn, i;

if ((fp = fopen("int.dat","r")) == NULL)

{

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