Главная · Поиск книг · Поступления книг · 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 ... 23 24 25 26 27 28 29  30 31 32 33 34 35 36 ... 87
        do { unsigned long sent;
             sprintf (name, "%s.%d", av[1], ++cnt);
             if ((fdout = creat (name, 0644)) < 0) {
    fprintf (stderr, "Cannot create %s\n", name); exit (3);
             }
             sent = 0L; /* сколько байт переслано */
             for(;;){ unsigned isRead, /* прочитано read-ом */
                need = min(ONEFILESIZE - sent, PORTION);
                if( need == 0 ) break;
                sent += (isRead = read (fdin, buf, need));
                errno = 0;
                if (write (fdout, buf, isRead) != isRead &&
                    errno){ perror("write"); exit(4);
                } else if (isRead < need){ done++; break; }
             }
             if(close (fdout) < 0){
                perror("Мало места на диске"); exit(5);
             }
             printf("%s\t%lu байт\n", name, sent);
        } while( !done ); exit(0);
    }

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

4.31.  Напишите обратную программу, которая склеивает несколько файлов в  один.   Это
аналог  команды  cat  с  единственным  отличием:  результат выдается не в стандартный
вывод, а в файл, указанный в строке аргументов последним. Для  выдачи  в  стандартный
вывод следует указать имя "-".

    #include 
    #include 
    void main (int ac, char **av){
        int     i, err = 0;    FILE *fpin, *fpout;
        if (ac < 3) {
           fprintf(stderr,"Usage: %s from... to\n", av[0]);
           exit(1);
        }
        fpout = strcmp(av[ac-1], "-")  ? /* отлично от "-" */
                fopen (av[ac-1], "wb") : stdout;
        for (i = 1; i < ac-1; i++) {
            register int c;
            fprintf (stderr, "%s\n", av[i]);
            if ((fpin = fopen (av[i], "rb")) == NULL) {
                fprintf (stderr, "Cannot read %s\n", av[i]);
                err++; continue;
            }
            while ((c = getc (fpin)) != EOF)
                putc (c, fpout);
            fclose (fpin);
        }
        fclose (fpout); exit (err);
    }

Обе эти программы могут без изменений транслироваться и в  MS  DOS  и  в  UNIX.  UNIX
просто  игнорирует букву b в открытии файла "rb", "wb". При работе с read мы могли бы
открывать файл как

    #ifdef M_UNIX
    # define O_BINARY       0
    #endif
    int fdin = open( av[1], O_RDONLY | O_BINARY);

4.32.  Каким образом стандартный ввод переключить на ввод из заданного файла, а стан-
дартный  вывод  -  в  файл?  Как проверить, существует ли файл; пуст ли он?  Как надо
открывать файл для дописывания информации в конец существующего файла?  Как надо отк-
рывать файл, чтобы попеременно записывать и читать тот же файл?  Указание: см. fopen,
freopen, dup2, stat.  Ответ про перенаправления ввода:

            способ 1        (библиотечные функции)
      #include 
      ...
      freopen( "имя_файла", "r", stdin );

            способ 2        (системные вызовы)
      #include 
      int fd;
      ...
      fd = open( "имя_файла", O_RDONLY );
      dup2 ( fd, 0 ); /* 0 - стандартный ввод    */
      close( fd );    /* fd больше не нужен - закрыть
           его, чтоб не занимал место в таблице */

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

            способ 3        (системные вызовы)
      #include 
      int fd;
      ...
      fd = open( "имя_файла", O_RDONLY );
      close (0);               /* 0 - стандартный ввод */
      fcntl (fd, F_DUPFD, 0 ); /* 0 - стандартный ввод */
      close (fd);

Это перенаправление ввода соответствует конструкции

    $ a.out < имя_файла

написанной на командном языке СиШелл.  Для перенаправления вывода замените  0  на  1,
stdin на stdout, open на creat, "r" на "w".
     Рассмотрим механику работы вызова dup2 [*]:

    new = open("файл1",...); dup2(new, old); close(new);

      таблица открытых
       файлов процесса
        ...##                    ##
    new----##---> файл1    new---##---> файл1
           ##                    ##
    old----##---> файл2    old---##     файл2
           ##                    ##
     0:до вызова     1:разрыв связи old с файл2
       dup2()     (закрытие канала old, если он был открыт)

           ##                    ##
    new----##--*--> файл1   new  ##  *----> файл1
           ##  |                 ##  |
    old----##--*            old--##--*
           ##                    ##
     2:установка old на файл1  3:после оператора close(new);
       на этом dup2 завершен.    дескриптор new закрыт.

Здесь файл1 и файл2 - связующие структуры "открытый файл" в ядре, о которых рассказы-
валось выше (в них содержатся указатели чтения/записи). После вызова dup2 дескрипторы
new и old ссылаются на общую такую структуру и поэтому  имеют  один  и  тот  же  R/W-
указатель.   Это  означает,  что  в  программе  new и old являются синонимами и могут
использоваться даже вперемежку:

    dup2(new, old);
    write(new, "a", 1);
    write(old, "b", 1);
    write(new, "c", 1);

запишет в файл1 строку "abc".  Программа

____________________
   [*] Функция
   int system(char *команда);
выполняет команду, записанную в строке команда, вызывая для этого  интерпретатор  ко-
манд
   /bin/sh -c "команда"

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

    int fd;
    printf( "Hi there\n");
    fd = creat( "newout", 0640 );
    dup2(fd, 1); close(fd);
    printf( "Hey, You!\n");

выдаст первое сообщение на терминал, а второе  -  в  файл  newout,  поскольку  printf
выдает данные в канал stdout, связанный с дескриптором 1.

4.33.  Напишите программу, которая будет выдавать  подряд  в  стандартный  вывод  все
файлы, чьи имена указаны в аргументах командной строки.  Используйте argc для органи-
зации цикла.  Добавьте сквозную нумерацию строк и печать номера строки.

4.34.  Напишите программу, распечатывающую первую директиву препроцессора,  встретив-
шуюся в файле ввода.

    #include 
    char buf[512], word[] = "#";
    main(){  char *s; int len = strlen(word);
      while((s=fgets(buf, sizeof buf, stdin)) &&
             strncmp(s, word, len));
      fputs(s? s: "Не найдено.\n", stdout);
    }

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

            >>>>>>имяФайла

Ответ:

    #include 
    char line[512];
    main(){  FILE *fp = fopen("00", "w");
       while(gets(line) != NULL)
         if( !strncmp(line, ">>>", 3)){
            if( freopen(line+3, "a", fp) == NULL){
              fprintf(stderr, "Can't write to '%s'\n", line+3);
              fp = fopen("00", "a");
            }
         } else fprintf(fp, "%s\n", line);
    }

4.36.  Библиотека буферизованного обмена stdio содержит функции,  подобные  некоторым
системным вызовам.  Вот функции - аналоги read и write:
     Стандартная функция fread из библиотеки стандартных функций Си предназначена для
чтения нетекстовой (как правило) информации из файла:

____________________
и возвращает код ответа этой программы.  Функция popen (pipe  open)  также  запускает
интерпретатор  команд,  при  этом  перенаправив его стандартный вывод в трубу (pipe).
Другой конец этой трубы можно читать через канал fp, т.е. можно прочесть в свою прог-
рамму выдачу запущенной команды.
____________________
   [*] dup2 читается как "dup to", в английском жаргоне принято обозначать предлог "to"
цифрой  2,  поскольку  слова  "to"  и "two" произносятся одинаково: "ту".  "From me 2
You". Также 4 читается как "for".

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

    int fread(addr, size, count, fp)
       register char *addr; unsigned size, count; FILE *fp;
    {  register c; unsigned ndone=0, sz;
       if(size)
         for( ; ndone < count ; ndone++){
            sz = size;
            do{   if((c = getc(fp)) >= 0 )
                        *addr++ = c;
                  else  return ndone;
            }while( --sz );
         }
       return ndone;
    }

Заметьте, что count - это не количество БАЙТ (как в read), а количество ШТУК размером
size байт.  Функция выдает число целиком прочитанных ею ШТУК.  Существует аналогичная
функция fwrite для записи в файл.  Пример:

    #include 
    #define MAXPTS 200
    #define N      127
    char filename[] = "pts.dat";
    struct point { int x,y; } pts[MAXPTS], pp= { -1, -2};
    main(){
       int n, i;
       FILE *fp = fopen(filename, "w");

       for(i=0; i < N; i++) /* генерация точек */
          pts[i].x = i, pts[i].y = i * i;
       /* запись массива из N точек в файл */
       fwrite((char *)pts, sizeof(struct point), N, fp);
       fwrite((char *)&pp, sizeof pp,            1, fp);

       fp = freopen(filename, "r", fp);
       /* или fclose(fp); fp=fopen(filename, "r"); */

       /* чтение точек из файла в массив */
       n = fread(pts, sizeof pts[0], MAXPTS, fp);
       for(i=0; i < n; i++)
          printf("Точка #%d(%d,%d)\n",i,pts[i].x,pts[i].y);
    }

Файлы, созданные fwrite, не переносимы на машины другого типа, поскольку в  них  хра-
нится  не текст, а двоичные данные в формате, используемом данным процессором.  Такой
файл не может быть понят человеком - он не содержит изображений данных в виде текста,
а содержит "сырые" байты.  Поэтому чаще пользуются функциями работы с текстовыми фай-
лами: fprintf, fscanf, fputs, fgets.  Данные, хранимые в виде текста, имеют еще  одно
преимущество  помимо  переносимости: их легко при нужде подправить текстовым редакто-
ром.  Зато они занимают больше места!
     Аналогом системного вызова lseek служит функция fseek:

    fseek(fp, offset, whence);

Она полностью аналогична lseek, за исключением  возвращаемого  ею  значения.  Она  НЕ
возвращает  новую позицию указателя чтения/записи!  Чтобы узнать эту позицию применя-
ется специальная функция

    long ftell(fp);

Она вносит поправку на положение указателя в буфере канала fp.  fseek сбрасывает флаг
"был достигнут конец файла", который проверяется макросом feof(fp);

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

4.37.  Найдите ошибку в программе (программа распечатывает корневой каталог  в  "ста-
ром" формате каталогов - с фиксированной длиной имен):

    #include 
    #include 
    #include 

    main(){
      FILE *fp;
      struct direct d;
      char buf[DIRSIZ+1]; buf[DIRSIZ] = '\0';

      fp = fopen( '/', "r" );
      while( fread( &d, sizeof d, 1, fp) == 1 ){
        if( !d.d_ino ) continue;  /* файл стерт */
        strncpy( buf, d.d_name, DIRSIZ);
        printf( "%s\n", buf );
      }
      fclose(fp);
    }

Указание: смотри в fopen().  Внимательнее к строкам и  символам!  '/'  и  "/"  -  это
совершенно разные вещи (хотя синтаксической ошибки нет!).
     Переделайте эту программу, чтобы название каталога поступало из аргументов  main
(а если название не задано - используйте текущий каталог ".").

4.38.  Функциями

       fputs(    строка, fp);
      printf(    формат, ...);
     fprintf(fp, формат, ...);

невозможно вывести строку формат, содержащую в середине байт '\0', поскольку он  слу-
жит  для  них  признаком конца строки.  Однако такой байт может понадобиться в файле,
если мы формируем некоторые нетекстовые данные, например  управляющую  последователь-
ность  переключения  шрифтов  для  принтера. Как быть?  Есть много вариантов решения.
Пусть мы хотим выдать в канал fp последовательность из 4х байт "\033e\0\5".  Мы можем
сделать это посимвольно:

    putc('\033',fp); putc('e',   fp);
    putc('\000',fp); putc('\005',fp);
Предыдущая страница Следующая страница
1 ... 23 24 25 26 27 28 29  30 31 32 33 34 35 36 ... 87
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 

Реклама