Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по вводу-выводу.doc
Скачиваний:
0
Добавлен:
18.09.2019
Размер:
583.17 Кб
Скачать

 

Тема 13. Стандартные библиотеки языка Си

 

 

 

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

 

В соответствии со стандартами ANSI должно быть 15 обязательных заголовочных файлов.

 

Таблица 1

Имя заголовочного файла

Назначение

assert.h

Диагностика программы

ctype.h

Преобразование и проверка типов

errno.h

Проверка ошибок

float.h

Функции для работы с типом с плавающей точкой

limits.h

Предельные значения целых типов

locale.h

Поддержка интернациональной среды (содержит

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

math.h

Математические функции

setjmp.h

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

signal.h

Обработка сигналов

stdarg.h

Поддержка функций с неопределенным числом параметров

stddef.h

Разное (содержит объявления типов, используемых для некоторых вычислений)

stdio.h

Библиотека стандартного ввода-вывода

stdlib.h

Функции общего назначения (выделение памяти и т.д.)

string.h

Функции для работы со строками

time.h

Функции для работы с датами и временем

 

 

13.1. Стандартная библиотека ввода-вывода. Работа с файлами

 

 

В языке Си нет встроенных средств ввода-вывода. Все эти действия выполняются соответствующими функциями ввода-вывода.

 

Библиотека Си поддерживает три уровня ввода-вывода:

1.       Ввод-вывод потока

2.       Ввод-вывод нижнего уровня

3.       Ввод-вывод для консоли и порта

 

 

13.1.1. Ввод-вывод потока

 

 

При вводе-выводе потока все данные рассматриваются как поток отдельных байтов, не зависимо от того, какое используется устройство ввода-вывода, т.е. поддерживается некоторый абстрактный уровень между программой и физическим устройством. Эта абстракция и называется потоком.

 

Поток  – это источник или получатель данных. Для пользователя поток- это либо файл на диске, либо физическое устройство.

 

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

 

Существует два типа потоков:

        текстовый

        бинарный

 

Текстовый поток  – это последовательность строк, каждая из которых содержит 0 или более символов и заканчивается символами возврата каретки ’\n.

 

В текстовом потоке может не быть однозначного соответствия между символами, которые передаются в потоке и символами, которые выводятся на внешнее устройство. ОС может потребовать коррекции текстового потока (например, пара символов « возврат каретки – перевод строки» преобразуется в одиночный символ перевода строки на вводе, а комбинация клавиш Ctrl-z (0x1a) интерпретируется как символ конца потока на вводе)

 

Бинарный поток  – это последовательность непреобразуемых байтов, которые взаимнооднозначно соответствуют тому, что выводится на внешнее устройство.

 

Поток связывается с физическим устройством или файлом путем его открытия. Как только файл открыт, то информация может передаваться между ним и программой. Эта связь разрывается путем закрытия потока. Открытие потока возвращает указатель на структуру типа FILE.

      

С началом выполнения программы автоматически открывается 5 потоков, которым соответствуют предопределенные указатели потоков:

1.      stdin      -стандартный ввод

2.      stdout    -стандартный вывод

3.      stderr     -стандартный вывод сообщений об ошибках

4.      stdaux  -стандартный дополнительный поток (COM1)

5.      stdprn   -стандартная печать (LPT1)

 

По умолчанию первые три (stdinstdout и stderrсвязаны с консолью пользователя и открываются в текстовом режиме. Это означает, что каждый раз, когда программа ожидает ввод со стандартного ввода, данные должны вводиться с консоли. Т.е. стандартный ввод – это ввод с клавиатуры, а стандартный вывод- это вывод на экран.

Назначения стандартному дополнительному потоку и стандартной печати зависят от конфигурации машины. Обычно stdaux связывается с дополнительным портом COM1, а stdprn – связывается с портом LPT1, к которому подключен принтер. Два последних потока открываются в двоичном режиме.

 

 

РЕКОМЕНДУЕТСЯ

Пользуйтесь преимуществами стандартных потоков ввода-вывода Си при написании своих программ.

 

НЕ РЕКОМЕНДУЕТСЯ

Не переименовывайте и не заменяйте стандартные потоки без особой необходимости.

Не пытайтесь воспользоваться потоком ввода stdin для целей вывода, например с помощью функции fprintf().

 

 

Режим доступа к файлам задается во время открытия файла как параметр функции открытия файла или специальной внешней переменной _fmode, описанной в заголовочных файлах «fcntl.h» или «stdlib.h» и может принимать 2 значения:

O_BINARY – двоичный режим;

- текстовый режим.

По умолчанию устанавливается знчение_fmode - O_TEXT.

 

 

13.2. Доступ к файлам через поток ввода-вывода

 

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

 

Указатель файла  – это указатель на структуру типа FILE, шаблон которой определен в «stdio.h», который используется для ссылки на поток.

 Формат объявления указателя на файл:

            FILE *имя_указателя_файла;

 

 Например, FILE *fptr;

13.2.1. Открытие потока

 

 

Функция fopen() открывает поток и связывает с ним файл с заданным именем. Она возвращает указатель, связанный с этим файлом или NULL, если попытка открытия неудачна (например, файл отсутствует).

 

Формат:

       FILE*fopen (char* имя_файла, char* режим_открытия);

 

Параметр режим_открытия определяет режим открытия файла и может принимать для текстовых файлов следующие значения:

r”             - файл открывается для чтения (файл должен существовать);

w            - файл создается для записи (старое содержимое удаляется);

a”             - файл открывается для добавления в конец существующего файла (файл создается, если он не существует);

r+”          - файл открывается для исправления (чтения или записи) – файл должен существовать;

w+”         - файл создается для чтения и записи (старое содержимое удаляется)

a+”          - файл открывается или создается для исправления  уже существующей информации и добавления новой в конец файла. Файл соэдается, если он не существует.

 

 

ПРИМЕЧАНИЕ

Если в любой из режимов добавить букву «b», то файл будет открыт в двоичном режиме. Например: “rb”, “w+b” и т.д.

Если в любой из режимов добавить букву «t» или не добавлять вообще никакой буквы, то файл будет открыт в текстовом режиме (используется по умолчанию). Например: “rt”, “a+t”, “w+”, “a” и т.д.

 

 

Пример: простейшее открытие файла “dat.txt”

                  FILE *fp;

                  fp=fopen (“dat.txt”,”r”);

 

Пример: рекомендуется использовать следующий способ открытия файла

                  FILE *fp;

                  if ((fp=fopen (“dat.txt”,”r”))==NULL)

                  {

                        printf (“Невозможно открыть файл! Программа завершается!”);

                        exit (1);

                  }

                  else

                  . . .

 

 

ПРИМЕЧАНИЕ

Если не указать путь к файлу, то программа должна предполагать, что текущим каталогом является тот же, в котором находится и сама программа.

Для указания пути к файлу программы следует добавить в нее соответствующий код.

 

 

Функция freopen() переназначает указатель потока на другой поток.

 

Формат:

       FILE*freopen (char* filename, char* mode, FILE *stream);

 

Данная функция открывает файл с указанным именем (filename) и режимом открытия (mode), и связывает его с потоком stream. Она возвращает stream или NULL (при неудаче). Обычно эта функция используется для замены файлов, связанных с stdinstdout и stderr.

 

Пример: freopen (“file.dat”,”w”,stdout); - закрывает поток для стандартного вывода и переназначает stdout файлу file.dat.

 

 

Пример: fopen.c  использование функции fopen() для открытия файлов в разных режимах

#include <stdlib.h>

#include <stdio.h>

int main( void )

{

   FILE *fp;

   char ch, filename[40], mode[4];

    while (1)

    {

        printf("\nEnter a filename: ");

        gets(filename);

        printf("\nEnter a mode (max 3 characters): ");

        gets(mode);

        if ( (fp = fopen( filename, mode )) != NULL )

        {

            printf("\nSuccessful opening %s in mode %s.\n",

                    filename, mode);

            fclose(fp);

            puts("Enter x to exit, any other to continue.");

            if ( (ch = getc(stdin)) == 'x')

                break;

            else

                continue;

        }

        else

        {

            fprintf(stderr, "\nError opening file %s in mode %s.\n",

                    filename, mode);

            puts("Enter x to exit, any other to try again.");

            if ( (ch = getc(stdin)) == 'x')

                break;

            else

                continue;

        }

    }

    return 0;

}

 

 

Результат:

                    Enter a file name: junk.txt

 

                    Enter a mode (max 3 characters) : w

 

                    Successful opening junk.txt in mode w.

                    Enter x to exit, any other to continue.

                    J

                   

                    Enter a file name: morejunk.txt

 

                    Enter a mode (max 3 characters) : r

 

                    Error opening morejunk.txt in mode r.

                    Enter x to exit, any other to continue.

                    

 

 

13.2.2. Закрытие потока

 

 

Функция fclose() закрывает отдельный поток, а fcloseall() –закрывает все открытые потоки, за исключением стандартных.

 

Формат:

       int fclose (FILE *fp);

 

       int fcloseall ();

 

Эти две функции возвращают нуль – в случае успеха и EOF - в случае ошибки, где EOF (End Of File) – конец файла. При ошибке все данные из буфера считываются в файл. Если поток явно не закрывается, то он будет закрыт автоматически при выходе из программы.

 

 

ВНИМАНИЕ

 

Одновременно может быть открыто не более 20 потоков.

 

 

Функция remove() уничтожает заданный файл.

 

Формат:

       int remove (char *имя_файла);                           

 

Возвращает 0  в случае успешного удаления файла.

 

 

СОВЕТ

Рекомендуем, чтобы программа всегда спрашивала пользователя, действительно ли он хочет удалить файл.

 

 

РЕКОМЕНДУЕТСЯ

Откройте файл, прежде чем пытаться читать из него данные или записывать их.

Используйте операцию sizeof() при вызове функций fwrite() и fread().

Закрывайте все открытые файлы по окончанию работы с ними.

 

НЕ РЕКОМЕНДУЕТСЯ

Не предполагайте, что операции с файлами заканчиваются успешно. Всегда проверяйте, как завершились операции открытия, чтения, записи файлов.

Не злоупотребляйте функцией fcloseall(), если у вас нет причин закрывать сразу все потоки.

 

 

Пример: remove.c  использование функции remove() для удаления файла с диска

#include <stdio.h>

int main( void )

{

    char filename[80];

    printf("Enter the filename to delete: ");

    gets(filename);

    if ( remove(filename) == 0)

        printf("The file %s has been deleted.\n", filename);

    else

        fprintf(stderr, "Error deleting the file %s.\n", filename);

    return 0;

}

 

 

Результат:

                  Enter the filename to delete: *.bak

                  Error deleting the file *.bak

Результат:

                  Enter the filename to delete: remove.bak

                  The file remove.bak has been deleted.

                 

 

13.3. Функции потокового ввода-вывода

 

 После открытия файла программа может выполнять файловый ввод-вывод.

Функции потокового ввода-вывода делятся на 4 группы:

  1. Функции посимвольного ввода-вывода

  2. Функции построчного ввода-вывода

  3. Функции блокового ввода-вывода

  4. Функции форматированного ввода-вывода

 

Таблица 2

Функции, использующие

стандартные потоки

Функции, требующие

Указания имени потока

Назначение

printf()

fprintf()

Форматированный вывод

vprintf()

vfprintf()

Форматированный вывод с

переменным списком параметров

 

puts()

fputs()

Вывод строк

putchar()

putc(), fputc()

Вывод символов

scanf()

fscanf()

Форматированный вывод

vscanf()

vfscanf()

Форматированный вывод с

переменным списком параметров

 

gets()

fgets()

Ввод строк

getchar()

getc(), fgetc()

Ввод символов

perror()

 

Ввод строк в stderr

 

 

Пример: stream.c  простой пример работы со стандартными потоками

#include <stdio.h>

int main( void )

{

   char buffer[256];

    // ввод строки и немедленный ее вывод

   puts(gets(buffer));

   return 0;

}

  

13.3.1. Функции посимвольного ввода-вывода

 

 

Функции посимвольного ввода-вывода  – это функции, за одно обращение к которым, переносится только один символ.

 

Функции посимвольного ввода:

      int fgetc (FILE *имя_указателя); - возвращает символ из открытого файла, описываемого переменной типа FILE, на которую указывает указатель имя_указателя.

 

      int getc (FILE *имя_указателя); - аналогична предыдущей функции, но является макроопределением.

 

      int fgetchar (void); - возвращает символ из файла стандартного ввода stdin.

 

      int getchar (void); - аналогична предыдущей функции, но является макроопределением.

 

      int ungetc (int символ, FILE *имя_указателя); - возвращает символ (его ASCII-код) назад в поток, на который ссылается указатель имя_указателя. Следующая операция чтения из этого потока вернет тот же символ.

 

Функции посимвольного ввода в случае успеха возвращают прочитанный символ (его ASCII-код), Преобразованный в int  а при ошибке – EOF.

 

РЕКОМЕНДУЕТСЯ

Уясните себе разницу между вводом с буферизацией и без нее.

Помните, что при вводе символов некоторые функции отображают вводимые символы на экране, а некоторые – нет.

 

НЕ РЕКОМЕНДУЕТСЯ

Не используйте функции не определенные в стандарте ANSI, если программа должна быть переносимой из одной среды в другую.

Не ожидайте от нестандартных функций одинакового поведения в средах различных компиляторов.

  

Пример: getchar1.c – использование функции getchar()

#include <stdio.h>

int main( void )

{

   int ch;

   while ((ch = getchar()) != '\n')

        putchar(ch);

   return 0;

}

 

Результат:

                  Напечатанный пользователем текст.

                  Напечатанный пользователем текст.

 

 

Пример: getchar2.c – ввод целых строк текста с помощью функции getchar()

#include <stdio.h>

#define MAX 80

int main( void )

{

    char ch, buffer[MAX+1];

    int x = 0;

    while ((ch = getchar()) != '\n' && x < MAX)

        buffer[x++] = ch;

    buffer[x] = '\0';

    printf("%s\n", buffer);

    return 0;

}

 

 

Результат:

                  Это строка

                  Это строка

 

 

ПРИМЕЧАНИЕ

Существуют еще две функции getch() и getche(), не определенные стандартом ANSI.

 

 

Функция getch(), которая получает следующий символ из потока stdin. Она вводит символ без буферизации и без дублирования его на экране. Она не определена в стандарте ANSI.

 

 

ВНИМАНИЕ

 

В приведенном ниже примере используется функция getch(), не определенная в стандарте ANSI. Соблюдайте осторожность при работе с такими функциями, поскольку не все компиляторы гарантируют их поддержку.

Если при компиляции приведенной программы появляется сообщение об ошибке, то вероятная причина этого – отсутствие в нем поддержки функции getch(). Так, например, компиляторы от Symantec и Borlandее поддерживают, а от Microsoft – аналогичную функцию _getch()

 

 

Пример: getch1.c  использование функции getch()

#include <stdio.h>

#include <conio.h>

int main( void )

{

     int ch;

     while ((ch = getch()) != '\n')

         putchar(ch);

     return 0;

}

 

 

Результат: Тестируем функцию getch()

 

 

Функция getche() практически ничем не отличается от getch(), с тем единственным отличием, что функция getche() выводит введенный символ в поток stdout.

 

 

ПРИМЕЧАНИЕ

Функция getche() не определенна стандартом ANSI, но многие компиляторы ее поддерживают.

 

 

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

 

Пример1:

                  int ch;

                  FILE *fp;

                  ch=fgetc (fp);

                  while (ch!=EOF)

                  {

                       ch=fgetc (fp);

                        . . .

                   }

 Пример2:

             int ch;

             FILE *fp;

             while ((ch=fgetc(fp))!=EOF)

             {

                  . . .

              }

 

Функции посимвольного вывода:

 Функции посимвольного вывода помещают символ (его ASCII-код) в открытый поток вывода. Символ преобразуется в char. В случае ошибки или достижения конца файла функции возвращают EOF, в случае успеха – выведенный символ.

      int fputc (int символ, FILE *имя_указателя); - помещает символ в открытый файл, который описывается переменной типа FILE, на которую ссылается указатель имя_указателя.

      int putc (int символ, FILE *имя_указателя); - аналогична предыдущей функции, но является макроопределением.

 

      int fputchar (int символ); - помещает символ в открытый файл stdout.

 

      int putchar (int символ); - аналогична предыдущей функции, но является макроопределением.

 

 

Пример: putchar1.c  демонстрация функции putchar()

#include <stdio.h>

int main( void )

{

  int count;

  for (count = 14; count < 128; )

        putchar(count++);

  return 0;

}

 

 

 

Пример: putchar2.c  отображение строки с помощью функции putchar()

#include <stdio.h>

#define MAXSTRING 80

char message[] = "Displayed with putchar().";

int main( void )

{

    int count;

    for (count = 0; count < MAXSTRING; count++)

    {

        if (message[count] == '\0')

        {

            putchar('\n');

            break;

        }

        else

            putchar(message[count]);

    }

    return 0;

}

 

 

Результат: Displayed with putchar().

 

 

Когда считывается двоичный файл, то определить наличие конца файла таким образом нельзя. Для этого используется функция feof().

 

Формат:

      int feof (FILE *имя_указателя);

 

Данная функция возвращает ненулевое значение, если конец файла достигнут и 0 - в противном случае.

 Конструкция while(!feof(fp))

{ ch=getc(fp); …….} читает файл, открытый в любом режиме до конца.

Пример: программы, котораяоткрывает файл с именем dat для чтения, файл с именем out – для записи, читает содержимое файла dat, преобразует малые латинские буквы в большие и записывает результат в файл out.

                  #include <stdio.h>

                  #include <stdlib.h>

                  main ()

                  {

                        int c;

                        FILE *p, *q;

                        p=fopen (“dat”,”r”);

                        q=fopen (“out”,”w”);

                        if (!p)

                        {

                                   printf (“Невозможно открыть файл dat! Программа завершается!”);

                                   exit (1);

                        }

                        if (!q)

                        {

                                   printf (“Невозможно открыть файл out! Программа завершается!”);

                                   exit (1);

                        }

                        while ((c=fgetc(p))!=EOF)

                        {

                                   if (c>=’a’ && c<=’z’) c+=’A’-‘a’;

                                   fputc (c, q);

                        }

                        close (p);

                        close (q);

                  }   

 

Результат: содержимое файла «dat» будет записано в файл «out» с заменой всех строчных букв на прописные (заглавные). 

 

РЕКОМЕНДУЕТСЯ

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

 

НЕ РЕКОМЕНДУЕТСЯ

Не считывайте данные из области памяти после конца или до начала файла. Чтобы избежать этого, проверяйте положение указателя.

Не используйте константу EOF при работе с двоичными файлами.

 

 

Пример: feof.c  использование функции feof() для обнаружения конца файла

#include <stdlib.h>

#include <stdio.h>

#define BUFSIZE 100

int main( void )

{

    char buf[BUFSIZE];

    char filename[60];

    FILE *fp;

    puts("Enter name of text file to display: ");

    gets(filename);

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

    {

        fprintf(stderr, "Error opening file.");

        exit(1);

    }

    while ( !feof(fp) )

    {

        fgets(buf, BUFSIZE, fp);

        printf("%s",buf);

    }

    fclose(fp);

    return 0;

}

 

 

Результат:

                  Enter name of text file to display:

                  hello.txt

                  Hello World!

 

 

13.3.2. Функции построчного ввода-вывода

 

 

Функции построчного ввода-вывода  – это функции, за одно обращение к которым, переносится целая строка символов.

 

Функции построчного ввода в случае успеха возвращают указатель на прочитанную строку символов, в случае ошибки или достижения условия конца файла функции возвращают NULL.

 

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.