4.20.
Напишите программу, которая распечатывает самую длинную строку из файла  ввода и ее длину.
4.21.
Напишите программу, которая выдает n-ую строку  файла.   Номер  строки  и  имя
файла задаются как аргументы main().
4.22.
Напишите программу
    slice -сКакой +сколько файл
которая выдает 
сколько строк файла 
файл, начиная со строки  номер  
сКакой  (нумерация
строк с единицы).
    #include <stdio.h>
    #include <ctype.h>
    long line, count, nline, ncount; /* нули */
    char buf[512];
    void main(int argc, char **argv){
      char c; FILE *fp;
      argc--; argv++;
      /* Разбор ключей */
      while((c = **argv) == '-' || c == '+'){
        long atol(), val; char *s = &(*argv)[1];
        if( isdigit(*s)){
           val = atol(s);
           if(c == '-')     nline  = val;
           else             ncount = val;
        } else fprintf(stderr,"Неизвестный ключ %s\n", s-1);
        argc--; ++argv;
      }
      if( !*argv ) fp = stdin;
      else if((fp = fopen(*argv, "r")) == NULL){
        fprintf(stderr, "Не могу читать %s\n", *argv);
        exit(1);
      }
    for(line=1, count=0; fgets(buf, sizeof buf, fp); line++){
          if(line >= nline){
             fputs(buf, stdout); count++;
          }
          if(ncount && count == ncount)
             break;
      }
      fclose(fp); /* это не обязательно писать явно */
    }
    /* End_Of_File */
4.23.
  Составьте программу, которая распечатывает последние 
n строк файла ввода.
4.24.
Напишите программу, которая делит входной файл на файлы по n строк в каждом.
4.25.
Напишите программу, которая читает 2 файла  и  печатает  их  вперемежку:  одна строка  из первого файла, другая - из второго.  Придумайте, как поступить, если файлы содержат разное число строк.
4.26.
Напишите программу сравнения двух файлов, которая  будет  печатать  первую  из
различающихся строк и позицию символа, в котором они различаются.
4.27.
Напишите программу для интерактивной работы с файлом.  Сначала у вас  запрашивается имя файла, а затем вам выдается меню:
    -  Записать текст в файл.
    
-  Дописать текст к концу файла.
    
-  Просмотреть файл.
    
-  Удалить файл.
    
-  Закончить работу.
Текст вводится в файл построчно с клавиатуры. Конец ввода - EOF (т.е.  CTRL/D),  либо
одиночный символ '.' в начале строки.  Выдавайте число введенных строк.
Просмотр файла должен вестись постранично: после выдачи очередной  порции  строк выдавайте подсказку
    --more-- _
(курсор остается в той же строке и обозначен подчерком) и ожидайте  нажатия  клавиши.
Ответ  'q'  завершает просмотр.  Если файл, который вы хотите просмотреть, не существует - выдавайте сообщение об ошибке.
После выполнения действия программа вновь запрашивает имя файла.  Если вы  ответите вводом пустой строки (сразу нажмете <ENTER>, то должно использоваться имя файла,
введенное на предыдущем шаге.  Имя файла, предлагаемое по умолчанию, принято писать в запросе в [] скобках.
Введите имя файла [oldfile.txt]: _
Когда вы научитесь работать с экраном  дисплея  (см.  главу  "Экранные  библиотеки"),
перепишите меню и выдачу сообщений с использованием позиционирования курсора в заданное место экрана и с выделением текста инверсией.  Для выбора имени файла  предложите меню:  отсортированный список имен всех файлов текущего каталога (по поводу получения списка файлов см. главу про взаимодействие с UNIX).  Просто для  распечатки  текущего каталога на экране можно также использовать вызов
     system("ls -x");
а для считывания каталога в программу
* 
    FILE *fp = popen("ls *.c", "r");
    ... fgets(...,fp); ... // в цикле, пока не EOF
    pclose(fp);
(в этом примере читаются только имена 
.c файлов).
4.28.
Напишите программу удаления n-ой строки из файла; вставки строки  после  m-ой.
К  сожалению, это возможно только путем переписывания всего файла в другое место (без ненужной строки) и последующего его переименования.
4.29.
  Составьте программу перекодировки текста, набитого в кодировке КОИ-8,  в  альтернативную  кодировку и наоборот.  Для этого следует составить таблицу перекодировки
из 256 символов: c_new=TABLE[c_old]; Для решения обратной  задачи  используйте  стандартную функцию strchr().  Программа читает один файл и создает новый.
4.30.
Напишите программу, делящую большой файл на  куски  заданного  размера  (не  в
строках,  а  в килобайтах).  Эта программа может применяться для записи слишком большого файла на дискеты (файл режется на части и записывается на несколько дискет).
    #include <fcntl.h>
    #include <stdio.h>
    #define min(a,b)  (((a) < (b)) ? (a) : (b))
    #define KB                   1024  /* килобайт */
    #define PORTION         (20L* KB)  /* < 32768  */
    long    ONEFILESIZE  = (300L* KB);
    extern char    *strrchr(char *, char);
    extern long     atol   (char *);
    extern errno;           /* системный код ошибки  */
    char    buf[PORTION];   /* буфер для копирования */
    void main (int ac, char *av[]) {
        char    name[128], *s, *prog = av[0];
        int     cnt=0, done=0, fdin, fdout;
    /* M_UNIX автоматически определяется
     * компилятором в UNIX */
    #ifndef M_UNIX  /* т.е. MS DOS */
        extern int _fmode; _fmode = O_BINARY;
        /* Задает режим открытия и создания ВСЕХ файлов */
    #endif
        if(av[1] && *av[1] == '-'){ /* размер одного куска */
            ONEFILESIZE = atol(av[1]+1) * KB; av++; ac--;
        }
        if (ac < 2){
          fprintf(stderr, "Usage: %s [-size] file\n", prog);
          exit(1);
        }
        if ((fdin = open (av[1], O_RDONLY)) < 0) {
    fprintf (stderr, "Cannot read %s\n", av[1]); exit (2);
        }
        if ((s = strrchr (av[1], '.'))!= NULL) *s = '\0';
        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);
    }
4.31.
Напишите обратную программу, которая склеивает несколько файлов в  один.   Это
аналог  команды  cat  с  единственным  отличием:  результат выдается не в стандартный вывод, а в файл, указанный в строке аргументов последним. Для  выдачи  в  стандартный вывод следует указать имя "-".
    #include <fcntl.h>
    #include <stdio.h>
    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 <stdio.h>
      ...
      freopen( "имя_файла", "r", stdin );
            способ 2        (системные вызовы)
      #include <fcntl.h>
      int fd;
      ...
      fd = open( "имя_файла", O_RDONLY );
      dup2 ( fd, 0 ); /* 0 - стандартный ввод    */
      close( fd );    /* fd больше не нужен - закрыть
           его, чтоб не занимал место в таблице */
            способ 3        (системные вызовы)
      #include <fcntl.h>
      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 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 <stdio.h>
    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 <stdio.h>
    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 из библиотеки стандартных функций Си предназначена для
чтения нетекстовой (как правило) информации из файла:
    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 <stdio.h>
    #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);
4.37.
  Найдите ошибку в программе (программа распечатывает корневой каталог  в  "старом" формате каталогов - с фиксированной длиной имен):
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/dir.h>
    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);
(можно просто в цикле), либо использовать один из способов:
    fprintf( fp,         "\033e%c\5", '\0');
    write  ( fileno(fp), "\033e\0\5",   4 );
    fwrite ( "\033e\0\5", sizeof(char), 4, fp);
где 4 - количество выводимых байтов.
4.39.
Напишите функции для "быстрого доступа" к строкам файла. Идея такова:  сначала прочитать  весь  файл  от  начала  до  конца и смещения начал строк (адреса по файлу) запомнить в массив чисел типа long  (точнее,  off_t),  используя  функции  fgets()  и
ftell(). Для быстрого чтения n-ой строки используйте функции fseek() и fgets().
    #include <stdio.h>
    #define MAXLINES 2000  /* Максим. число строк в файле*/
    FILE *fp;              /* Указатель на файл */
    int nlines;            /* Число строк в файле */
    long offsets[MAXLINES];/* Адреса начал строк */
    extern long ftell();/*Выдает смещение от начала файла*/
    char buffer[256];      /* Буфер для чтения строк */
    /* Разметка массива адресов начал строк */
    void getSeeks(){
       int c;
       offsets[0] =0L;
       while((c = getc(fp)) != EOF)
         if(c =='\n') /* Конец строки - начало новой */
            offsets[++nlines] = ftell(fp);
    /* Если последняя строка файла не имеет \n на конце, */
    /* но не пуста, то ее все равно надо посчитать */
       if(ftell(fp) != offsets[nlines])
            nlines++;
       printf( "%d строк в файле\n", nlines);
    }
    char *getLine(n){  /* Прочесть строку номер n */
       fseek(fp, offsets[n], 0);
       return fgets(buffer, sizeof buffer, fp);
    }
    void main(){ /* печать файла задом-наперед */
       int i;
       fp = fopen("INPUT", "r"); getSeeks();
       for( i=nlines-1; i>=0; --i)
            printf( "%3d:%s", i, getLine(i));
    }
* - Функция
   int system(char *команда);
выполняет команду, записанную в строке 
команда, вызывая для этого  интерпретатор  команд
   /bin/sh -c "команда"
** dup2 читается как "dup to", в английском жаргоне принято обозначать предлог "to"
цифрой  2,  поскольку  слова  "to"  и "two" произносятся одинаково: "ту".  "From me 2 You". Также 4 читается как "for".
© Copyright А. Богатырев, 1992-95  
     Си в UNIX
Назад | Содержание | Вперед