Мегаобучалка Главная | О нас | Обратная связь


Командная строка аргументов



2019-12-29 234 Обсуждений (0)
Командная строка аргументов 0.00 из 5.00 0 оценок




 

Системные средства, на которые опирается реализация языка «С», позволяют передавать командную строку аргументов или параметров начинающей выполняться программе. Когда функция main вызывается к исполнению, она вызывается с двумя аргументами. Первый аргумент (условно называемый argc) указывает число аргументов в командной строке, с которыми происходит обращение к программе; второй аргумент (argv) является указателем на массив символьных строк, содержащих эти аргументы, по одному в строке. Работа с такими строками – это обычное использование многоуровневых указателей.

Пример 6-16. Самую простую иллюстрацию этой возможности и необходимых при этом описаний дает программа echo, которая просто печатает в одну строку аргументы командной строки, разделяя их пробелами. Таким образом, если дана команда:

echo Hello, World

то выходом будет

Hello, World

по соглашению argv[0] является именем, по которому вызывается программа, так что argc по меньшей мере равен 1. В приведенном выше примере argc равен 3, а argv[0], argv[1] и argv[2] равны соответственно "echo", "Hello," и "World". Первым фактическим агументом является argv[1], а последним будет argv[argc-1]. Если argc равен 1, то за именем программы не следует никакой командной строки аргументов. Все это показано в echo:

 

// Аргументы echo: 1-я версия

main(int argc, char *argv[])

{

int i;

for (i = 1; i < argc; i++)

printf("%s%c", argv[i], (i<argc-1) ? ' ' : '\n');

}

 

Поскольку argv является указателем на массив указателей, то существует несколько способов написания этой программы, использующих работу с указателем, а не с индексацией массива. Мы продемонстрируем два варианта.


           Пример 6-17. Один вариант.

 

// Аргументы echo: 2-я версия

main(int argc, char *argv[])

{

while (--argc > 0)

printf("%s%c",*++argv, (argc > 1) ? ' ' : '\n');

}        

 

Так как argv является указателем на начало массива строк-аргументов, то, увеличив его на 1 посредством ++argv, мы вынуждаем его указывать на подлинный аргумент argv[1], а не на argv[0]. Каждое последующее увеличение передвигает его на следующий аргумент; при этом *argv становится указателем на этот аргумент. Одновременно величина argc уменьшается; когда она обратится в нуль, все аргументы будут уже напечатаны.

Пример 6-18. Другой вариант:

 

// Аргументы echo: 3-я версия

main(int argc, char *argv[])

{

while (--argc > 0)

printf((argc > 1) ? "%s" : "%s\n", *++argv);

}

 

Эта версия показывает, что аргумент формата функции printf может быть выражением, точно так же, как и любой другой. Такое использование встречается не очень часто, но его все же стоит запомнить.

Пример 6-19. Давайте внесем некоторые усовершенствования в программу отыскания заданной комбинации символов из главы 5. Если вы помните, мы поместили искомую комбинацию глубоко внутрь программы, что очевидно является совершенно неудовлетворительным. Следуя утилите grep системы U nix, давайте изменим программу так, чтобы эта комбинация указывалась в качестве первого аргумента строки.

 

// Печать строк, содержащих образец, заданный первым

// аргументом

#define maxline 1000

main(int argc, char *argv[])

{

char line[maxline];

if (argc != 2)

printf ("Используйте в find образец! \n");

else

while (getline(line, maxline) > 0)

    if (index(line, argv[1] >= 0)

       printf("%s", line);

}

 

Теперь может быть развита основная модель, иллюстрирующая дальнейшее использование указателей. Предположим, что нам надо предусмотреть два необязательных аргумента. Один утверждает: «Напечатать все строки за исключением тех, которые содержат данную комбинацию», а второй гласит: «Перед каждой выводимой строкой должен печататься ее номер».

Общепринятым соглашением в «С»-программах является то, что аргумент, начинающийся со знака минус, вводит необязательный признак или параметр. Если мы, для того, чтобы сообщить об инверсии, выберем –x (для указания «кроме»), а для указания о нумерации нужных строк выберем –n («номер»), то команда:

find -x -n the

при входных данных

Now is the time

For all good men

To come to the aid

Of their party.

должна выдать

2: For all good men

 

Пример 6-20. Часто необходимо, чтобы необязательные аргументы могли располагаться в произвольном порядке, и чтобы остальная часть программы не зависела от количества фактически присутствующих аргументов. В частности, вызов функции index не должен содержать ссылку на argv[2], когда присутствует один необязательный аргумент, и на argv[1], когда его нет. Более того, для пользователей удобно, чтобы необязательные аргументы можно было объединить в виде:

find -nx образец

 

А вот и сама программа:

 

// Печать строк, содержащих образец, заданный первым

// аргументом

#define maxline 1000

main(int argc, char *argv[])

{

char line[maxline], *s;

long lineno = 0;

int except = 0, number = 0;

while (--argc > 0 && (*++argv)[0] == '-')

for (s = argv[0]+1; *s != '\0'; s++)

   switch (*s)

     {

     case 'x':

       except = 1;

       break;

     case 'n':

       number = 1;

       break;

     default:

       printf("find: неверный параметр %c\n",*s);

       argc = 0;

       break;

     }

if (argc != 1)

printf("usage: find -x -n pattern\n");

else

while (getlinе(line, maxline) > 0)

   {

   lineno++;

   if ((index(line, *argv) >= 0) != except)

     {

     if (number)

        printf("%ld: ", lineno);

     printf("%s", line);

     }

   }

}

 

Аргумент argv увеличивается перед каждым необязательным аргументом, в то время как аргумент argc уменьшается. Если нет ошибок, то в конце цикла величина argc должна равняться 1, а *argv должно указывать на заданную комбинацию. Обратите внимание на то, что *++argv является указателем аргументной строки: (*++argv)[0] – её первый символ. Круглые скобки здесь необходимы, потому что без них выражение бы приняло совершенно отличный и неправильный вид: *++(ARGV[0]). Другой правильной формой была бы **++argv.

 


Упражнение 6-7. Напишите программу add, вычисляющую обратное польское выражение из командной строки. Например,

add 2 3 4 + *

вычисляет 2´(3+4).

 

Упражнение 6-8. Модифицируйте программы entab и detab (указанные в качестве упражнений в главе 2) так, чтобы они получали список табуляционных остановок в качестве аргументов. Если аргументы отсутствуют, используйте стандартную установку табуляций.

Упражнение 6-9. Расширьте entab и detab таким образом, чтобы они воспринимали сокращенную нотацию:

entab -m +n

означающую табуляционные остановки через каждые n позиций, начиная со столбца m. Выберите удобное (для пользователя) поведение функции по умолчанию.

 

Упражнение 6-10. Напишите программу для функции tail, печатающей последние n строк из своего файла ввода. Пусть по умолчанию n равно 10, но это число может быть изменено с помощью необязательного аргумента, так что:

tail -n

печатает последние n строк. Программа должна действовать рационально, какими бы неразумными ни были бы ввод или значение n. Составьте программу так, чтобы она оптимальным образом использовала доступную память: строки должны храниться, как в функции sort, а не в двумерном массиве фиксированного размера.

 

Указатели на функции

 

Пример 6-21. В языке «С» сами функции не являются переменными, но имеется возможность определить указатель на функцию, который можно обрабатывать, передавать другим функциям, помещать в массивы и т.д. Мы проиллюстрируем это, проведя модификацию написанной ранее программы сортировки так, чтобы при задании необязательного аргумента -n она бы сортировала строки ввода численно, а не лексикографически.

Сортировка часто состоит из трех частей:

· сравнения, которое определяет упорядочивание любой пары объектов,

· перестановки, изменяющей их порядок, и

· алгоритма сортировки, осуществляющего сравнения и перестановки до тех пор, пока объекты не расположатся в нужном порядке.


Алгоритм сортировки не зависит от операций сравнения и перестановки, так что, передавая в него различные функции сравнения и перестановки, мы можем организовать сортировку по различным критериям. Именно такой подход используется в нашей новой программе сортировки.

Как и прежде, лексикографическое сравнение двух строк осуществляется функцией strcmp, а перестановка функцией swap; нам нужна еще функция numcmp, сравнивающая две строки на основе численного значения и возвращающая условное указание того же вида, что и strcmp. Эти три функции описываются в main и указатели на них передаются в sort. В свою очередь функция sort обращается к этим функциям через их указатели. Мы урезали обработку ошибок в аргументах с тем, чтобы сосредоточиться на главных вопросах.

 

// Сортировка вводимых строк

#define lines 100 // Максимальное число строк   

main(int argc, char *argv[])

{

char *lineptr[lines]; // Указатели на строки

int nlines;        // Число введенных строк

int strcmp(), numcmp(); // Функции сравнения

int swap();        // Функция exchange

int numeric = 0; // 1, если сорт. по числ. значен.

if(argc>1 && argv[1][0] == '-' && argv[1][1]=='n')

numeric = 1;

if(nlines = readlines(lineptr, lines)) >= 0)

{

if (numeric)

    sort(lineptr, nlines, numcmp, swap);

else

    sort(lineptr, nlines, strcmp, swap);

writelines(lineptr, nlines);

}

else

printf("input too big to sort\n");

}

 

Здесь strcmp, nimcmp и swap – адреса функций; так как известно, что это функции, операция & здесь не нужна совершенно аналогично тому, как она не нужна и перед именем массива. Передача адресов функций организуется компилятором.


Второй шаг состоит в модификации sort:

// Сортировать строки v[0] ... v[n-1] по возрастанию

sort(char *v[],int n, int (*comp)(), int (*exch)())

{

int gap, i, j;

for(gap = n/2; gap > 0; gap /= 2)

for(i = gap; i < n; i++)

    for(j = i-gap; j >= 0; j -= gap)

       {

       if((*comp)(v[j], v[j+gap]) <= 0)

          break;

       (*exch)(&v[j], &v[j+gap]);

       }

}       

 

Здесь следует обратить определенное внимание на описания. Описание:

int (*comp)()

говорит, что comp является указателем на функцию, которая возвращает значение типа int. Первые круглые скобки здесь необходимы: без них описание:

int *comp()

говорило бы, что comp является функцией, возвращающей указатель на целые, что, конечно, совершенно другая вещь.

Использование comp в строке:

if (*comp)(v[j], v[j+gap]) <= 0)

полностью согласуется с описанием:

comp – указатель на функцию, *comp – сама функция, а

(*comp)(v[j], v[j+gap])

– это обращение к ней. Круглые скобки необходимы для правильного объединения компонентов.

 

Мы уже приводили функцию strcmp, сравнивающую две строки по первому численному значению:

// Сравнить численно s1 и s2

numcmp(char *s1, char *s2)

{

double atof(), v1, v2;

v1 = atof(s1);

v2 = atof(s2);

if(v1 < v2)

return(-1);

else if(v1 > v2)

return(1);

else

return (0);

}

Заключительный шаг состоит в добавлении функции swap, переставляющей два указателя. Это легко сделать, непосредственно используя то, что мы изложили ранее в этой главе:

// Переставить указатели *px и *py

swap(char *px[],char *py[])

{

char *temp;

temp = *px;

*px = *py;

*py = temp;

}

 

Имеется множество других необязятельных аргументов, которые могут быть включены в программу сортировки: некоторые из них составляют интересные упражнения.

Упражнение 6-11. Модифицируйте sort таким образом, чтобы она работала с меткой -r, указывающей на сортировку в обратном (убывающем) порядке. Конечно, -r должна работать с -n.

Упражнение 6-12. Добавьте необязательный аргумент -f, объединяющий вместе прописные и строчные буквы, так чтобы различие регистров не учитывалось во время сортировки: данные из верхнего и нижнего регистров сортируются вместе, так что буква 'а' прописное и 'а' строчное оказываются соседними, а не разделенными целым алфавитом.

Упражнение 6-13. Добавьте необязательный аргумент -d (т.н. «словарное упорядочивание»), при наличии которого сравниваются только буквы, числа и пробелы. Позаботьтесь о том, чтобы эта функция работала и вместе с -f.

Упражнение 6-14. Добавьте возможность обработки полей, так чтобы можно было сортировать поля внутри строк. Каждое поле должно сортироваться в соответствии с независимым набором необязательных аргументов. Предметный указатель этой книги сортировался с помощью следующих аргументов:

-df для терминов (категории указателя);

–n для номеров страниц.

 


СТРУКТУРЫ

 

Структура – это набор из одной или более переменных, возможно различных типов, сгруппированных под одним именем для удобства обработки (в некоторых языках, самый известный из которых ПАСКАЛЬ, структуры называются «записями»).

Традиционным примером структуры является учетная карточка работающего: «служащий» описывается набором атрибутов таких, как фамилия, имя, отчество (ф.и.о.), адрес, код социального обеспечения, зарплата и т.д. Некоторые из этих атрибутов сами могут оказаться структурами: ф.и.о. Имеет несколько компонент, как и адрес, и даже зарплата.

Структуры оказываются полезными при организации сложных данных особенно в больших программах, поскольку во многих ситуациях они позволяют сгруппировать связанные данные таким образом, что с ними можно обращаться, как с одним целым, а не как с отдельными объектами. В этой главе мы постараемся продемонстрировать то, как используются структуры. Программы, которые мы для этого будем использовать, больше, чем многие другие в этой книге, но все же достаточно умеренных размеров.

 

Основные сведения

 

Давайте снова обратимся к процедурам преобразования даты из главы 6. Дата состоит из нескольких частей таких, как день, месяц, и год, и, возможно, день года и имя месяца. Эти пять переменных можно объединить в одну структуру вида:

struct date

{

int day;

int month;

int year;

int yearday;

char mon_name[4];

};

 

Описание структуры, состоящее из заключенного в фигурные скобки списка описаний, начинается с ключевого слова struct. За словом struct может следовать необязательное имя, называемое ярлыком структуры (здесь это date). Такой ярлык именует структуры этого вида и может использоваться в дальнейшем как сокращенная запись подробного описания.

Переменные, упомянутые в структуре, называются элементами (или членами) структуры. Ярлыки и элементы структур могут иметь такие же имена, что и обычные переменные (т.е. не являющиеся элементами структур), поскольку их имена всегда можно различить по контексту. Конечно, обычно одинаковые имена присваивают только тесно связанным объектам.

Точно так же, как в случае любого другого базисного типа, за правой фигурной скобкой, закрывающей список элементов, может следовать список переменных. Оператор:

struct {...} x,y,z;

синтаксически аналогичен:

int x,y,z;

 

в том смысле, что каждый из операторов описывает x , y и z в качестве переменных соотвествующих типов и приводит к выделению для них памяти.

 

Описание структуры, за которым не следует списка переменных, не приводит к выделению какой-либо памяти; оно только определяет шаблон или форму структуры. Однако, если такое описание снабжено ярлыком, то этот ярлык может быть использован позднее при определении фактических экземпляров структур. Например, если дано приведенное выше описание date, то:

struct date d;

 

определяет переменную d в качестве структуры типа date. Внешнюю или статическую структуру можно инициализировать, поместив вслед за ее определением список инициализаторов для ее компонент:

struct date d={ 4, 7, 1776, 186, "jul"};

 

Элемент (член) определенной структуры может быть указан в выражении с помощью конструкции вида 

Имя структуры. Элемент

 

Операция указания члена структуры (точка ".") связывает имя структуры и имя элемента. В качестве примера определим leap (признак високосности года) на основе даты, находящейся в структуре d,

leap = d.year % 4 == 0 && d.year % 100 != 0

|| d.year % 400 == 0;

или проверим имя месяца:

if (strcmp(d.mon_name, "aug") == 0) ...

или преобразуем первый символ имени месяца так, чтобы оно начиналось со строчной буквы:

d.mon_name[0] = lower(d.mon_name[0]);


Структуры могут быть вложенными; учетная карточка служащего может фактически выглядеть так:

struct person 

{

char name[namesize];

char address[adrsize];

long zipcode;         // Почтовый индекс

long ss_number;   // Код соц. обеспечения

double salary;    // Зарплата

struct date birthdate; // Дата рождения

struct date hiredate; // Дата приема на работу

};

 

Структура person содержит две структуры типа date. Если мы определим emp как:

struct person emp;

то

emp.birthdate.month

будет ссылаться на месяц рождения.

 

Следует помнить, что операция указания члена структуры "." ассоциируется слева направо.

 

Структуры и функции

 

В языке «C» существует ряд ограничений на использование структур. Обязательные правила заключаются в том, что единственные операции, которые вы можете проводить со структурами, состоят в определении ее адреса с помощью операции & и доступе к одному из ее членов. Это влечет за собой то, что структуры нельзя присваивать или копировать как целое, и что они не могут быть переданы функциям или возвращены ими. (В последующих версиях эти ограничения будут сняты). На указатели структур эти ограничения, однако, не накладываются, так что структуры и функции все же могут с удобством работать совместно. И, наконец, автоматические структуры, как и автоматические массивы, не могут быть инициализированы; инициализация возможна только в случае внешних или статических структур.

Пример 7-1. Давайте разберем некоторые из этих вопросов, переписав с этой целью функции преобразования даты из предыдущей главы так, чтобы они использовали структуры. Так как правила запрещают непосредственную передачу структуры функции, то мы должны либо передавать отдельно компоненты, либо передать указатель всей структуры. Первая возможность демонстрируется на примере функции day_of_year, как мы ее написали в главе 6:

d.yearday = day_of_year(d.year, d.month, d.day);

другой способ состоит в передаче указателя. Если мы опишем hiredate как:

struct date hiredate;

и перепишем day_of_year нужным образом, мы сможем тогда написать:

hiredate.yearday = day_of_year(&hiredate);

передавая указатель на hiredate функции day_of_year. Функция должна быть модифицирована, потому что ее аргумент теперь является указателем, а не списком переменных.

 

// Установить день года по месяцу и дню

day_of_year(struct date *pd)

{

int i, day, leap;

day = pd->day;

leap = pd->year % 4 == 0 && pd->year % 100 != 0

     || pd->year % 400 == 0;

for (i =1; i < pd->month; i++)

day += day_tab[leap][i];

return(day);

}

 

Описание:

struct date *pd;

говорит, что pd является указателем структуры типа date. Запись, показанная на примере:

pd->year

является новой. Если p – указатель на структуру, то выражение вида:

p->член структуры

обращается к конкретному элементу. (Операция -> записывается как знак "минус", за которым без пробела следует знак "больше".)

 

Так как pd указывает на структуру, то к члену year можно обратиться и следующим образом:

(*pd).year ,

но указатели структур используются настолько часто, что запись "->" оказывается удобным сокращением. Круглые скобки в (*pd).year необходимы, потому что операция указания члена структуры старше, чем *. Обе операции, "->" и ".", ассоциируются слева направо, так что конструкции в двух примерах слева и справа эквивалентны:

p->q->memb (p->q)->memb
Emp.birthdate.month (emp.birthdate).month

Для полноты изложения ниже приводится другая функция, month_day, переписанная с использованием структур.

 

// Установить месяц и день по дню года

month_day(struct date *pd)

{

int i, leap;

leap = pd->year % 4 == 0 && pd->year % 100 != 0

     || pd->year % 400 == 0;

pd->day = pd->yearday;

for (i = 1; pd->day > day_tab[leap][i]; i++)

pd->day -= day_tab[leap][i];

pd->month = i;

}

 

Операции работы со структурами "->" и "." наряду с круглыми скобками () для списка аргументов и квадратными скобками [] для индексов находятся на самом верху иерархии старшинства операций и, следовательно, связываются очень крепко. Если, например, имеется описание:

struct

{

int x;

char *y;

} *p;

 

то выражение:

++p->x

увеличивает х, а не р, так как оно эквивалентно выражению ++(p->х).

 

Для изменения порядка выполнения операций можно использовать круглые скобки: (++p)->х увеличивает p до доступа к х, а (p++)->x увеличивает х после доступа. (Круглые скобки в последнем случае необязательны. Почему?)

Совершенно аналогично:

*p->y извлекает то, что содержится в элементе y;
*p->y++ увеличивает y после обработки того, на что он указывает (точно так же, как и *s++);
(*p->y)++ увеличивает то, на что указывает y;
*p++->y увеличивает P после выборки того, на что указывает y.

Массивы структур

 

Структуры особенно подходят для управления массивами связанных переменных. Рассмотрим, например, программу подсчета числа вхождений каждого ключевого слова языка «C». Нам нужен массив символьных строк для хранения имен и массив целых для подсчета.

Одна из возможностей состоит в использовании двух «параллельных массивов» keyword и keycount:

char *keyword[nkeys];

int keycount[nkeys];

 

Но сам факт, что массивы параллельны, указывает на возможность другой организации. Каждое ключевое слово здесь по существу является парой:

char *keyword;

int keycount;

 

и, следовательно, имеется массив пар. Описание структуры:

 

struct key 

{

char *keyword;

int keycount;

} keytab[nkeys];

 

оперделяет массив keytab структур такого типа и отводит для них память. Каждый элемент массива является структурой. Это можно было бы записать и так:

 

struct key 

{

char *keyword;

int keycount;

};

 

struct key keytab[nkeys];

 

Так как структура keytab фактически содержит постоянный набор имен, то легче всего инициализировать ее один раз и для всех членов при определении. Инициализация структур вполне аналогична предыдущим инициализациям – за определением следует заключенный в фигурные скобки список инициализаторов:

 


struct key 

{

char *keyword;

int keycount;

} keytab[] =

      {

      "break", 0,

      "case", 0,

      "char", 0,

      "continue", 0,

      "default", 0,

      ...

      "unsigned", 0,

      "while", 0

      };

 

Инициализаторы перечисляются парами соответственно членам структуры. Было бы более точно заключать в фигурные скобки инициализаторы для каждой «строки» или структуры следующим образом:

{ "break", 0 },

{ "case", 0 },

...

Но когда инициализаторы являются простыми переменными или символьными строками и все они присутствуют, то во внутренних фигурных скобках нет необходимости. Как обычно, компилятор сам вычислит число элементов массива keytab, если инициализаторы присутствуют, а скобки [] оставлены пустыми.

Пример 7-2. Программа подсчета ключевых слов начинается с определения массива keytab. ведущая программа читает свой файл ввода, последовательно обращаясь к функции getword, которая извлекает из ввода по одному слову за обращение. Каждое слово ищется в массиве keytab с помощью варианта функции бинарного поиска, написанной нами в главе 4. (Конечно, чтобы эта функция работала, список ключевых слов должен быть расположен в порядке возрастания).

 

#define maxword 20

main() // Подсчет ключевых слов «С»

{

int n, t;

char word[maxword];

while ((t = getword(word,maxword)) != eof)

if (t == letter)

    if((n = binary(word,keytab,nkeys)) >= 0)

       keytab[n].keycount++;

for (n =0; n < nkeys; n++)

if (keytab[n].keycount > 0)

    printf("%4d %s\n",

keytab[n].keycount, keytab[n].keyword);

}

 

// Функция binary: найти слова в tab[0]...tab[n-1]

binary(char *word, struct key tab[],int n)

{

int low, high, mid, cond;

low = 0;

high = n - 1;

while (low <= high)

{

mid = (low+high) / 2;

if((cond = strcmp(word, tab[mid].keyword)) < 0)

    high = mid - 1;

else if (cond > 0)

    low = mid + 1;

else

    return (mid);

}

return(-1);

}

 

Мы вскоре приведем функцию getword; пока достаточно сказать, что она возвращает letter каждый раз, как она находит слово, и копирует это слово в свой первый аргумент.

Величина nkeys – это количество ключевых слов в массиве keytab. Хотя мы можем сосчитать это число вручную, гораздо легче и надежнее поручить это машине, особенно в том случае, если список ключевых слов подвержен изменениям. Одной из возможностей было бы закончить список инициализаторов указанием на нуль и затем пройти в цикле сквозь массив keytab, пока не найдется конец.

Но, поскольку размер этого массива полностью определен к моменту компиляции, здесь имеется более простая возможность. Число элементов просто есть:

sizeof keytab / sizeof struct key

 

Дело в том, что в языке «C» предусмотрена унарная операция sizeof, выполняемая во время компиляции, которая позволяет вычислить размер любого объекта. Выражение sizeof(object) выдает целое, равное размеру указанного объекта. (Размер определяется в неспецифицированных единицах, называемых «байтами», которые имеют тот же размер, что и переменные типа char).

Объект может быть фактической переменной, массивом и структурой, или именем основного типа, как int или double, или именем производного типа, как структура. В нашем случае число ключевых слов равно размеру массива, деленному на размер одного элемента массива. Это вычисление используется в утверждении #define для установления значения nkeys:

 

#define nkeys (sizeof(keytab) / sizeof(struct key))

 

Теперь перейдем к функции getword. Мы фактически написали более общий вариант функции getword, чем необходимо для этой программы, но он не на много более сложен. Функция getword возвращает следующее «слово» из ввода, где словом считается либо строка букв и цифр, начинающихся с буквы, либо отдельный символ. Тип объекта возвращается в качестве значения функции. Это может быть:

· letter, если найдено слово;

· eof для конца файла;

· сам символ, если он не буквенный.

 

// Получить следующее слово из потока символов ввода

getword(char *w, int lim)  

{

int c, t;

if (type(c=*w++=getch()) != letter)

{

*w='\0';

return(c);

}

while (--lim > 0) 

{

t = type(c = *w++ = getch());

if (t != letter && t != digit)

    {

    ungetch(c);

    break;

    }

}

*(w-1) - '\0';

return(letter);

}

 


Функция getword использует функции getch и ungetch, которые мы написали в главе 5: когда набор алфавитных символов прерывается, функция getword получает один лишний символ. В результате вызова ungetch этот символ помещается назад во ввод для следующего обращения. Функция getword обращается к функции type для определения типа каждого отдельного символа из файла ввода. Вот вариант, справедливый только для алфавита ASCII.

 

// Получить тип символа ASCII

int type(int c) 

{

if (c>= 'a' && c<= 'z' || c>= 'a' && c<= 'z')

return(letter);

else if (c>= '0' && c<= '9')

return(digit);

else

return(c);

}

 

Символические константы letter и digit могут иметь любые значения, лишь бы они не вступали в конфликт с символами, отличными от буквенно-цифровых, и с eof; очевидно возможен следующий выбор:

 

#define letter 'a'

#define digit '0'

 

Функция getword могла бы работать быстрее, если бы обращения к функции type были заменены обращениями к соответствующему массиву type[]. В стандартном заголовочном h-файле <ctype.h> языка «C» предусмотрены макросы isalpha и isdalnum, действующие необходимым образом.

Упражнение 7-1. Сделайте такую модификацию функции getword и оцените, как изменится скорость работы программы.

Упражнение 7-2. Напишите вариант функции type, не зависящий от конкретного набора символов.

Упражнение 7-3. Напишите вариант программы подсчета ключевых слов, который бы не учитывал появления этих слов в заключенных в кавычки строках.

 




2019-12-29 234 Обсуждений (0)
Командная строка аргументов 0.00 из 5.00 0 оценок









Обсуждение в статье: Командная строка аргументов

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Личность ребенка как объект и субъект в образовательной технологии: В настоящее время в России идет становление новой системы образования, ориентированного на вхождение...
Как вы ведете себя при стрессе?: Вы можете самостоятельно управлять стрессом! Каждый из нас имеет право и возможность уменьшить его воздействие на нас...
Как распознать напряжение: Говоря о мышечном напряжении, мы в первую очередь имеем в виду мускулы, прикрепленные к костям ...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (234)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.011 сек.)