Напишите программу, которая распечатывает самую длинную строку из файла ввода и ее длину.
Напишите программу, которая выдает n-ую строку файла. Номер строки и имя файла задаются как аргументы main().
Напишите программу
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 */
Напишите программу, которая делит входной файл на файлы по n строк в каждом.
Напишите программу, которая читает 2 файла и печатает их вперемежку: одна строка из первого файла, другая - из второго. Придумайте, как поступить, если файлы содержат разное число строк.
Напишите программу сравнения двух файлов, которая будет печатать первую из различающихся строк и позицию символа, в котором они различаются.
Напишите программу для интерактивной работы с файлом. Сначала у вас запрашивается имя файла, а затем вам выдается меню:
Текст вводится в файл построчно с клавиатуры. Конец ввода - EOF (т.е. CTRL/D), либо одиночный символ '.' в начале строки. Выдавайте число введенных строк.
Просмотр файла должен вестись постранично: после выдачи очередной порции строк выдавайте подсказку
--more-- _(курсор остается в той же строке и обозначен подчерком) и ожидайте нажатия клавиши. Ответ 'q' завершает просмотр. Если файл, который вы хотите просмотреть, не существует - выдавайте сообщение об ошибке.
После выполнения действия программа вновь запрашивает имя файла. Если вы ответите вводом пустой строки (сразу нажмете <ENTER>, то должно использоваться имя файла, введенное на предыдущем шаге. Имя файла, предлагаемое по умолчанию, принято писать в запросе в [] скобках.
Введите имя файла [oldfile.txt]: _
Когда вы научитесь работать с экраном дисплея (см. главу "Экранные библиотеки"), перепишите меню и выдачу сообщений с использованием позиционирования курсора в заданное место экрана и с выделением текста инверсией. Для выбора имени файла предложите меню: отсортированный список имен всех файлов текущего каталога (по поводу получения списка файлов см. главу про взаимодействие с UNIX). Просто для распечатки текущего каталога на экране можно также использовать вызов
system("ls -x");а для считывания каталога в программу*
FILE *fp = popen("ls *.c", "r"); ... fgets(...,fp); ... // в цикле, пока не EOF pclose(fp);(в этом примере читаются только имена .c файлов).
Напишите программу удаления n-ой строки из файла; вставки строки после m-ой. К сожалению, это возможно только путем переписывания всего файла в другое место (без ненужной строки) и последующего его переименования.
Составьте программу перекодировки текста, набитого в кодировке КОИ-8, в альтернативную кодировку и наоборот. Для этого следует составить таблицу перекодировки из 256 символов: c_new=TABLE[c_old]; Для решения обратной задачи используйте стандартную функцию strchr(). Программа читает один файл и создает новый.
Напишите программу, делящую большой файл на куски заданного размера (не в строках, а в килобайтах). Эта программа может применяться для записи слишком большого файла на дискеты (файл режется на части и записывается на несколько дискет).
#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); }
Напишите обратную программу, которая склеивает несколько файлов в один. Это аналог команды 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);
Каким образом стандартный ввод переключить на ввод из заданного файла, а стандартный вывод - в файл? Как проверить, существует ли файл; пуст ли он? Как надо открывать файл для дописывания информации в конец существующего файла? Как надо открывать файл, чтобы попеременно записывать и читать тот же файл? Указание: см. 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.
Напишите программу, которая будет выдавать подряд в стандартный вывод все файлы, чьи имена указаны в аргументах командной строки. Используйте argc для организации цикла. Добавьте сквозную нумерацию строк и печать номера строки.
Напишите программу, распечатывающую первую директиву препроцессора, встретившуюся в файле ввода.
#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); }
Напишите программу, которая переключает свой стандартный вывод в новый файл имяФайла каждый раз, когда во входном потоке встречается строка вида
>>>имяФайлаОтвет:
#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); }
Библиотека буферизованного обмена 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);
#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 (а если название не задано - используйте текущий каталог ".").
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 - количество выводимых байтов.
Напишите функции для "быстрого доступа" к строкам файла. Идея такова: сначала прочитать весь файл от начала до конца и смещения начал строк (адреса по файлу) запомнить в массив чисел типа 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 www.abyss-group.narod.ru
Си в UNIX
Назад | Содержание | Вперед