}
}
А. Богатырев, 1992-95 - 281 - Си в UNIX
/* -----------------------------------------------------------*/
/* Отсортировать и напечатать окружение */
void printenv(){
char *e[40]; register i = 0; char *p, **q = e;
do{
p = e[i] = environ[i]; i++;
} while( p );
#ifdef SORT
qsort( e, --i /* сколько */, sizeof(char *), cmps);
#endif
while( *q )
printf( "%s\n", *q++ );
}
/* Сравнение имени переменной окружения с name */
static char *envcmp(name, evstr) char *name, *evstr;
{
char *p; int code;
if((p = strchr(evstr, '=')) == NULL ) return NULL; /* error ! */
*p = '\0'; /* временно */
code = strcmp(name, evstr);
*p = '='; /* восстановили */
return code==0 ? p+1 : NULL;
}
/* Установить переменную окружения */
void setenv( name, value ) char *name, *value;
{
static malloced = 0; /* 1, если environ перемещен */
char *s, **p, **newenv;
int len, change_at = (-1), i;
/* Есть ли переменная name в environ-е ? */
for(p = environ; *p; p++ )
if(s = envcmp(name, *p)){ /* уже есть */
if((len = strlen(s)) >= strlen(value)){
/* достаточно места */
strcpy(s, value); return;
}
/* Если это новый environ ... */
if( malloced ){
free( *p ); *p = str3spl(name, "=", value);
return;
}
/* иначе создаем копию environ-а */
change_at = p - environ; /* индекс */
break;
}
А. Богатырев, 1992-95 - 282 - Си в UNIX
/* Создаем копию environ-а. Если change_at == (-1), то
* резервируем новую ячейку для еще не определенной переменной */
for(p=environ, len=0; *p; p++, len++ );
/* вычислили количество переменных */
if( change_at < 0 ) len++;
if((newenv = (char **) malloc( sizeof(char *) * (len+1)))
== (char **) NULL) return;
for(i=0; i < len+1; i++ ) newenv[i] = NULL; /* зачистка */
/* Копируем старый environ в новый */
if( !malloced ) /* исходный environ в стеке (дан системой) */
for(i=0; environ[i]; i++ ) newenv[i] = strdup(environ[i]);
else for(i=0; environ[i]; i++ ) newenv[i] = environ[i];
/* Во втором случае строки уже были спасены, копируем ссылки */
/* Изменяем, если надо: */
if( change_at >= 0 ){
free( newenv[change_at] );
newenv[change_at] = str3spl(name, "=", value);
} else { /* добавить в конец новую переменную */
newenv[len-1] = str3spl(name, "=", value);
}
/* подменить environ */
if( malloced ) free( environ );
environ = newenv; malloced++;
qsort( environ, len, sizeof(char *), cmps);
}
/* Допишите команды:
unsetenv имя_переменной - удаляет переменную среды;
exit N - завершает интерпретатор с
кодом возврата N (это целое число);
*/
А. Богатырев, 1992-95 - 283 - Си в UNIX
7. Текстовая обработка.
Под "текстовой обработкой" (в противовес "вычислительным задачам") здесь понима-
ется огромный класс задач обработки информации нечислового характера, например редак-
тирование текста, форматирование документов, поиск и сортировка, базы данных, лекси-
ческий и синтаксический анализ, печать на принтере, преобразование формата таблиц,
и.т.п.
7.1. Напишите программу, "угадывающую" слово из заранее заданного списка по первым
нескольким буквам. Выдайте сообщение "неоднозначно", если есть несколько похожих
слов. Усложните программу так, чтобы список слов считывался в программу при ее
запуске из файла list.txt
7.2. Напишите программу, которая удваивает пробелы в тексте с одиночными пробелами.
7.3. Напишите программу, которая копирует ввод на вывод, заменяя каждую последова-
тельность из идущих подряд нескольких пробелов и/или табуляций на один пробел. Схема
ее решения сходна с решением следующей задачи.
7.4. Напишите программу подсчета слов в файле. Слово определите как последователь-
ность символов, не включающую символы пробела, табуляции или новой строки. "Канони-
ческий" вариант решения, приведенный у Кернигана и Ритчи, таков:
#include
#include
const int YES=1, NO=0;
main(){
register int inWord = NO; /* состояние */
int words = 0, c;
while((c = getchar()) != EOF)
if(isspace(c) || c == '\n') inWord = NO;
else if(inWord == NO){
inWord = YES; ++words;
}
printf("%d слов\n", words);
}
Обратите внимание на конструкцию const. Это объявление имен как констант. Эта конст-
рукция близка к
#define YES 1
но позволяет компилятору
- более строго проверять тип, т.к. это типизированная константа;
- создавать более экономный код;
- запрещает изменять это значение.
Рассмотрим пример
main(){ /* cc 00.c -o 00 -lm */
double sqrt(double);
const double sq12 = sqrt(12.0);
#define SQRT2 sqrt(2.0)
double x;
x = sq12 * sq12 * SQRT2 * SQRT2; /* @1 */
sq12 = 3.4641; /* @2 */
printf("%g %g\n", sq12, x);
}
Использование #define превратит строку @1 в
x = sq12 * sq12 * sqrt(2.0) * sqrt(2.0);
то есть создаст код с двумя вызовами функции sqrt. Конструкция же const заносит
вычисленное выражение в ячейку памяти и далее просто использует ее значение. При этом
А. Богатырев, 1992-95 - 284 - Си в UNIX
компилятор не позволяет впоследствии изменять это значение, поэтому строка @2 оши-
бочна.
Теперь предложим еще одну программу подсчета слов, где слово определяется макро-
сом isWord, перечисляющим буквы допустимые в слове. Программа основана на переключа-
тельной таблице функций (этот подход применим во многих случаях):
#include
#include
int wordLength, inWord, words; /* = 0 */
char aWord[128], *wrd;
void space (c){}
void letter (c){ wordLength++; *wrd++ = c; }
void begWord(c){ wordLength=0; inWord=1;
wrd=aWord; words++; letter(c); }
void endWord(c){ inWord=0; *wrd = '\0';
printf("Слово '%s' длины %d\n",
aWord, wordLength); }
void (*sw[2][2])() = {
/* !isWord */ { space, endWord },
/* isWord */ { begWord, letter }
/* !inWord inWord */
};
#define isWord(c) (isalnum(c) || c=='-' || c=='_')
main(){ register c;
while((c = getchar()) != EOF)
(*sw[isWord(c)][inWord])(c);
printf("%d слов\n", words);
}
7.5. Напишите программу, выдающую гистограмму длин строк файла (т.е. таблицу: строк
длины 0 столько-то, длины 1 - столько-то, и.т.п., причем таблицу можно изобразить
графически).
7.6. Напишите программу, которая считывает слово из файла in и записывает это слово
в конец файла out.
7.7. Напишите программу, которая будет печатать слова из файла ввода, причем по
одному на строку.
7.8. Напишите программу, печатающую гистограмму длин слов из файла ввода.
7.9. Напишите программу, читающую слова из файла и размещающую их в виде двунаправ-
ленного списка слов, отсортированного по алфавиту. Указания: используйте динамическую
память (malloc) и указатели; напишите функцию включения нового слова в список на нуж-
ное место.
В конце работы распечатайте список дважды: в прямом и в обратном порядке.
Усложнение: не хранить в списке дубликаты; вместо этого вместе со словом хранить
счетчик количества его вхождений в текст.
7.10. Напишите программу, которая печатает слова из своего файла ввода, расположен-
ные в порядке убывания частоты их появления. Перед каждым словом напечатайте число
частоты его появления.
7.11. Напишите программу, читающую файл построчно и печатающую слова в каждой строке
в обратном порядке.
А. Богатырев, 1992-95 - 285 - Си в UNIX
7.12. Напишите программу копирования ввода на вывод таким образом, чтобы из каждой
группы последовательно одинаковых строк выводилась только одна строка. Это аналог
программы uniq в системе UNIX. Ответ:
#include /* char *gets(); */
char buf1[4096], buf2[4096];
char *this = buf1, *prev = buf2;
main(){
long nline =0L; char *tmp;
while( gets(this)){
if(nline){ /* сравнить новую и предыдущую строки */
if( strcmp(this, prev)) /* различны ? */
puts(prev);
}
/* обмен буферов: */ tmp=prev; prev=this; this=tmp;
nline++; /* номер строки */
}/* endwhile */
if( nline ) puts(prev);
/* последняя строка всегда выдается */
}
7.13. Составьте программу, которая будет удалять в конце (и в начале) каждой строки
файла пробелы и табуляции, а также удалять строки, целиком состоящие из пробелов и
табуляций.
7.14. Для экономии места в файле, редакторы текстов при записи отредактированного
файла сжимают подряд идущие пробелы в табуляцию. Часто это неудобно для программ
обработки текстов (поскольку требует особой обработки табуляций - это ОДИН символ,
который на экране и в тексте занимает НЕСКОЛЬКО позиций!), поэтому при чтении файла
мы должны расширять табуляции в нужное количество пробелов, например так:
/* заменять табуляции на пробелы */
void untab(s) register char *s;
{
char newstr[256]; /* новая строка */
char *src = s;
int n; /* счетчик */
register dstx; /* координата x в новой строке */
for(dstx = 0; *s != '\0'; s++)
if( *s == '\t'){
for(n = 8 - dstx % 8 ; n > 0 ; n--)
newstr[dstx++] = ' ';
}else newstr[dstx++] = *s;
newstr[dstx] = '\0';
strcpy(src, newstr); /* строку на старое место */
}
7.15. Напишите обратную функцию, сжимающую подряд идущие пробелы в табуляции.
А. Богатырев, 1992-95 - 286 - Си в UNIX
void tabify(){
int chr;
int icol, ocol; /* input/output columns */
for(icol = ocol = 0; ; ){
if((chr = getchar()) == EOF)
break;
switch(chr){
case ' ':
icol++;
break;
case '\n':
case '\r':
ocol = icol = 0;
putchar(chr);
break;
case '\t':
icol += 8;
icol &= ~07; /* icol -= icol % 8; */
break;
default:
while(((ocol + 8) & ~07) <= icol){
#ifdef NOTDEF
if(ocol + 1 == icol)
break;
/* взять ' ' вместо '\t' */
#endif