Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Demon's Souls |#13| Storm King
Demon's Souls |#11| Мaneater part 2
Demon's Souls |#10| Мaneater (part 1)
Demon's Souls |#9| Heart of surprises

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Богатырев А. Весь текст 1009.15 Kb

Хрестоматия по программированию на Си в Unix

Предыдущая страница Следующая страница
1 ... 21 22 23 24 25 26 27  28 29 30 31 32 33 34 ... 87

А. Богатырев, 1992-95                  - 148 -                              Си в UNIX

в буфер считывается read-ом из файла порция информации, и getc выдает ее первый байт.
При  последующих вызовах getc выдаются следующие байты из буфера, а обращений к диску
уже не происходит!  Лишь когда буфер будет исчерпан - произойдет очередное  чтение  с
диска.  Таким  образом,  информация читается из файла с опережением, заранее наполняя
буфер; а по требованию выдается уже из буфера.  Если мы читаем 1024  байта  из  файла
при  помощи  getc(),  то мы 1024 раза вызываем эту функцию, но всего 2 раза системный
вызов read - для чтения двух порций информации из файла, каждая - по 512 байт.
     При записи

            char c; FILE *fp = ... ;
            putc(c, fp);

выводимые символы накапливаются в буфере.  Только когда в нем окажется большая порция
информации, она за одно обращение write записывается на диск.  Буфер записи "выталки-
вается" в файл в таких случаях:
-    буфер заполнен (содержит BUFSIZ символов).
-    при закрытии файла (fclose или exit [*][*]).
-    при вызове функции fflush (см. ниже).
-    в специальном режиме - после помещения в буфер символа '\n' (см. ниже).
-    в некоторых версиях - перед любой операцией чтения из  канала  stdin  (например,
     при  вызове  gets), при условии, что stdout буферизован построчно (режим _IOLBF,
     смотри ниже), что по-умолчанию так и есть.

Приведем упрощенную схему, поясняющую взаимоотношения основных функций и макросов  из
stdio  (кто  кого  вызывает).  Далее s означает строку, c - символ, fp - указатель на
структуру FILE [**][**].  Функции, работающие со строками, в  цикле  вызывают  посимвольные
операции.   Обратите  внимание, что в конце концов все функции обращаются к системным
вызовам read и write, осуществляющим ввод/вывод низкого уровня.
     Системные вызовы далее обозначены жирно, макросы - курсивом.

            Открыть файл, создать буфер:

    #include 
    FILE *fp = fopen(char *name, char *rwmode);
                   |  вызывает
                   V
       int fd = open (char *name, int irwmode);
    Если открываем на запись и файл не существует (fd < 0),
    то создать файл вызовом:
           fd = creat(char *name, int accessmode);
           fd будет открыт для записи в файл.

По умолчанию fopen() использует для creat коды доступа accessmode  равные  0666  (rw-
rw-rw-).

____________________
   [*][*] При выполнении вызова завершения программы exit(); все открытые файлы автомати-
чески закрываются.
   [**][**] Обозначения fd для дескрипторов и fp для указателей на файл прижились и их сле-
дует  придерживаться.   Если  переменная должна иметь более мнемоничное имя - следует
писать так: fp_output, fd_input (а не просто fin, fout).

А. Богатырев, 1992-95                  - 149 -                              Си в UNIX

      Соответствие аргументов fopen и open:
            rwmode          irwmode
            -------------------------
            "r"             O_RDONLY
            "w"             O_WRONLY|O_CREAT |O_TRUNC
            "r+"            O_RDWR
            "w+"            O_RDWR  |O_CREAT |O_TRUNC
            "a"             O_WRONLY|O_CREAT |O_APPEND
            "a+"            O_RDWR  |O_CREAT |O_APPEND

Для r, r+ файл уже должен существовать, в остальных случаях файл создается, если  его
не было.
     Если fopen() не смог открыть (или создать) файл, он возвращает значение NULL:

    if((fp = fopen(name, rwmode)) == NULL){ ...неудача... }

Итак, схема:

    printf(fmt,...)--->--,----fprintf(fp,fmt,...)->--*
                     fp=stdout                       |
                              fputs(s,fp)--------->--|
    puts(s)----------->-------putchar(c)-----,---->--|
                                         fp=stdout   |
                      fwrite(array,size,count,fp)->--|
                                                     |
        Ядро ОС                               putc(c,fp)
    ------------------*                              |
    |файловая---<--write(fd,s,len)------------<----БУФЕР
    |система---->---read(fd,s,len)-*     _flsbuf(c,fp)
    |   |             !            |
    |системные буфера !            |
    |   |             !            V           ungetc(c,fp)
    |драйвер устр-ва  !            |                      |
    |(диск, терминал) !            |     _filbuf(fp)      |
    |   |             !            *--------->-----БУФЕР<-*
    |устройство       !                              |
    ------------------*                       c=getc(fp)
                                                     |
              rdcount=fread(array,size,count,fp)--<--|
    gets(s)-------<---------c=getchar()------,----<--|
                                         fp=stdout   |
                                                     |
                            fgets(sbuf,buflen,fp)-<--|
    scanf(fmt,.../*ук-ли*/)--<-,--fscanf(fp,fmt,...)-*
                            fp=stdin

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

    fclose(fp) ---> close(fd);

И чуть в стороне - функция позиционирования:

    fseek(fp,long_off,whence) ---> lseek(fd,long_off,whence);

Функции _flsbuf и _filbuf - внутренние для stdio, они как раз сбрасывают буфер в файл
либо читают новый буфер из файла.
     По указателю fp можно узнать дескриптор файла:

    int fd = fileno(fp);

Это макроопределение просто выдает поле из структуры FILE.  Обратно, если мы  открыли

А. Богатырев, 1992-95                  - 150 -                              Си в UNIX

файл open-ом, мы можем ввести буферизацию этого канала:

    int fd = open(name, O_RDONLY);  /* или creat() */
            ...
    FILE *fp = fdopen(fd, "r");

(здесь надо вновь указать КАК мы открываем файл, что  должно  соответствовать  режиму
открытия open-ом).  Теперь можно работать с файлом через fp,  а не  fd.
     В приложении имеется текст, содержащий упрощенную реализацию главных функций  из
библиотеки stdio.

4.11.  Функция ungetc(c,fp) "возвращает" прочитанный байт в файл.  На самом деле байт
возвращается  в  буфер,  поэтому эта операция неприменима к небуферизованным каналам.
Возврат соответствует сдвигу указателя чтения из буфера  (который  увеличивается  при
getc())  на 1 позицию назад. Вернуть можно только один символ подряд (т.е. перед сле-
дующим ungetc-ом должен быть хоть один getc),  поскольку  в  противном  случае  можно
сдвинуть  указатель  за  начало  буфера  и, записывая туда символ c, разрушить память
программы.

    while((c = getchar()) != '+' );
    /* Прочли '+' */   ungetc(c ,stdin);
    /* А можно заменить этот символ на другой! */
    c = getchar();     /* снова прочтет '+' */

4.12.  Очень часто делают ошибку в функции fputc, путая порядок ее  аргументов.   Так
ничего не стоит написать:

            FILE *fp = ......;
            fputc( fp, '\n' );

Запомните навсегда!

            int fputc( int c,  FILE *fp );

указатель файла идет вторым!  Существует также макроопределение

            putc( c, fp );

Оно ведет себя как и функция fputc, но не может быть передано в качестве аргумента  в
функцию:

    #include 
    putNtimes(    fp,     c,     n,       f      )
            FILE *fp; int c; int n; int (*f)();
    {       while( n > 0 ){ (*f)( c, fp ); n--; }}

                 возможен вызов
            putNtimes( fp, 'a', 3, fputc );
                 но недопустимо
            putNtimes( fp, 'a', 3, putc );

Тем не менее всегда, где возможно, следует пользоваться макросом - он работает  быст-
рее. Аналогично, есть функция fgetc(fp) и макрос getc(fp).
     Отметим еще, что putchar и getchar это тоже всего лишь макросы

    #define putchar(c)      putc((c), stdout)
    #define getchar()       getc(stdin)

А. Богатырев, 1992-95                  - 151 -                              Си в UNIX

4.13.  Известная вам функция printf также является частью библиотеки stdio.  Она вхо-
дит в семейство функций:

    FILE   *fp; char bf[256];
    fprintf(fp, fmt, ... );
     printf(    fmt, ... );
    sprintf(bf, fmt, ... );

Первая из функций форматирует свои аргументы  в  соответствии  с  форматом,  заданным
строкой  fmt  (она содержит форматы в виде %-ов) и записывает строку-результат посим-
вольно (вызывая putc) в файл fp.  Вторая - это всего-навсего  fprintf  с  каналом  fp
равным  stdout.  Третяя  выдает  сформатированную строку не в файл, а записывает ее в
массив bf. В конце строки sprintf добавляет нулевой байт '\0' - признак конца.
     Для чтения данных по формату используются функции семейства

    fscanf(fp, fmt, /* адреса арг-тов */...);
     scanf(    fmt, ... );
    sscanf(bf, fmt, ... );

Функции fprintf и fscanf являются наиболее мощным средством работы с текстовыми  фай-
лами (содержащими изображение данных в виде печатных символов).

4.14.  Текстовые файлы (имеющие строчную организацию) хранятся на диске как  линейные
массивы  байт.   Для  разделения строк в них используется символ '\n'. Так, например,
текст

    стр1
    стрк2
    кнц

хранится как массив

    с т р 1 \n с т р к 2 \n к н ц     длина=14 байт
               !
       указатель чтения/записи (read/write pointer RWptr)
       (расстояние в байтах от начала файла)

При выводе на экран дисплея символ \n преобразуется драйвером терминалов в последова-
тельность \r\n, которая возвращает курсор в начало строки ('\r') и опускает курсор на
строку вниз ('\n'), то есть курсор переходит в начало следующей строки.
     В MS DOS строки в файле на диске разделяются двумя символами \r\n и  при  выводе
на  экран  никаких  преобразований  не делается[*].  Зато библиотечные функции языка Си
преобразуют эту последовательность при чтении из файла в \n,  а  при  записи  в  файл
превращают \n в \r\n, поскольку в Си считается, что строки разделяются только \n. Для
работы с файлом без таких преобразований, его надо открывать как "бинарный":

    FILE *fp = fopen( имя, "rb" );  /* b - binary */
    int fd   = open ( имя, O_RDONLY | O_BINARY );
____________________
   [*] Управляющие символы имеют следующие значения:

    '\n' - '\012' (10)  line feed
    '\r' - '\015' (13)  carriage return
    '\t' - '\011'  (9)  tab
    '\b' - '\010'  (8)  backspace
    '\f' - '\014' (12)  form feed
    '\a' - '\007'  (7)  audio bell (alert)
    '\0' - 0.           null byte

А. Богатырев, 1992-95                  - 152 -                              Си в UNIX

Все нетекстовые файлы в MS DOS надо открывать именно так, иначе могут произойти  раз-
ные  неприятности. Например, если мы программой копируем нетекстовый файл в текстовом
режиме, то одиночный символ \n будет считан в программу как \n, но  записан  в  новый
файл  как пара \r\n. Поэтому новый файл будет отличаться от оригинала (что для файлов
с данными и программ совершенно недопустимо!).
     Задание: напишите программу подсчета строк и символов в файле.   Указание:  надо
подсчитать  число символов '\n' в файле и учесть, что последняя строка файла может не
иметь этого символа на конце. Поэтому если последний символ файла  (тот,  который  вы
прочитаете самым последним) не есть '\n', то добавьте к счетчику строк 1.

4.15.  Напишите программу подсчета количества вхождений каждого из символов  алфавита
в файл  и печатающую результат в виде таблицы в 4 колонки. (Указание: заведите массив
из 256 счетчиков.  Для больших файлов счетчики должны быть типа long).

4.16.  Почему вводимый при помощи функций getchar() и getc(fp) символ  должен  описы-
ваться типом int а не char?
     Ответ: функция getchar() сообщает о конце файла тем, что возвращает значение EOF
(end  of  file),  равное целому числу (-1).  Это НЕ символ кодировки ASCII, поскольку
getchar() может прочесть из файла любой символ кодировки (кодировка содержит  символы
с кодами 0...255), а специальный признак не должен совпадать ни с одним из хранимых в
Предыдущая страница Следующая страница
1 ... 21 22 23 24 25 26 27  28 29 30 31 32 33 34 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама