Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Функция main(): разбор параметров командной строки



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

prog -d -o of lie dataO

Фактические параметры являются аргументами функции main() и могут быть получены из массива C-строк с именем argv; мы покажем, как их использовать.

Во всех предыдущих примерах определение main() содержало пустой список:

int main() {... }

Развернутая сигнатура main() позволяет получить доступ к параметрам, которые были заданы пользователем в командной строке:

int main( int argc, char *argv[] ){...}

argc содержит их количество, а argv – C-строки, представляющие собой отдельные значения (в командной строке они разделяются пробелами). Скажем, при запуске команды

prog -d -o ofile data0

argc получает значение 5, а argv включает следующие строки:

 

argv[ 0 ] = " prog";

argv[ 1 ] = " -d";

argv[ 2 ] = " -o";

argv[ 3 ] = " ofile";

argv[ 4 ] = " dataO";

 

В argv[0] всегда входит имя команды (программы). Элементы с индексами от 1 до argc-1 служат параметрами.

Посмотрим, как можно извлечь и использовать значения, помещенные в argv. Пусть программа из нашего примера вызывается таким образом:

prog [-d] [-h] [-v]

[-o output_file] [-l limit_value]

file_name

[ file_name [file_name [... ]]]

Параметры в квадратных скобках являются необязательными. Вот, например, запуск программы с их минимальным количеством – одним лишь именем файла:

prog chap1.doc

Но можно запускать и так:

 

 

prog -l 1024 -o chap1-2.out chapl.doc chap2.doc

prog d chap3.doc

prog -l 512 -d chap4.doc

При разборе параметров командной строки выполняются следующие основные шаги:

1. По очереди извлечь каждый параметр из argv. Мы используем для этого цикл for с начальным индексом 1 (пропуская, таким образом, имя программы):

for ( int ix = 1; ix < argc; ++ix ) {

char *pchar = argv[ ix ];

//...

}

2. Определить тип параметра. Если строка начинается с дефиса (-), это одна из опций { h, d, v, l, o}. В противном случае это может быть либо значение, ассоциированное с опцией (максимальный размер для -l, имя выходного файла для -o), либо имя входного файла. Чтобы определить, начинается ли строка с дефиса, используем инструкцию switch:

switch ( pchar[ 0 ] ) {

case '-': {

// -h, -d, -v, -l, -o

}

 

default: {

// обработаем максимальный размер для опции -1

//       имя выходного файла для  -o

//       имена входных файлов...

}

}

Реализуем обработку двух случаев пункта 2.

Если строка начинается с дефиса, мы используем switch по следующему символу для определения конкретной опции. Вот общая схема этой части программы:

case '-': {

switch( pchar[ 1 ] )

{

case 'd':

// обработка опции debug

break;

 

case 'v':

// обработка опции version

 break;

 

case 'h':

// обработка опции help

break;

 

case 'o':

// приготовимся обработать выходной файл

break;

 

case 'l':

// приготовимся обработать макс.размер

break;

 

default:

// неопознанная опция:

// сообщить об ошибке и завершить выполнение

}

}

Опция -d задает необходимость отладки. Ее обработка заключается в присваивании переменной с объявлением

bool debug_on = false;

значения true:

case 'd':

debug_on = true;

break;

В нашу программу может входить код следующего вида:

if ( debug_on )

display_state_elements( obj );

Опция -v выводит номер версии программы и завершает исполнение:

 

 

case 'v':

cout < < program_name < < ":: "

  < < program_version < < endl;

return 0;

Опция -h запрашивает информацию о синтаксисе запуска и завершает исполнение. Вывод сообщения и выход из программы выполняется функцией usage():

case 'h':

// break не нужен: usage() вызывает exit()

usage();

Опция -o сигнализирует о том, что следующая строка содержит имя выходного файла. Аналогично опция -l говорит, что за ней указан максимальный размер. Как нам обработать эти ситуации?

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

// если ofi1e_on==true,

// следующий параметр - имя выходного файла

bool ofi1e_on = false;

 

// если ofi1e_on==true,

// следующий параметр - максимальный размер

bool limit_on = false;

Вот обработка опций -l и -o в нашей инструкции switch:

case 'l':

limit_on = true;

break;

 

case 'o':

ofile_on = true;

break;

Встретив строку, не начинающуюся с дефиса, мы с помощью переменных состояния можем узнать ее содержание:

// обработаем максимальный размер для опции -1

//       имя выходного файла для  -o

//       имена входных файлов...

default: {

// ofile_on включена, если -o встречалась

if ( ofile_on ) {

// обработаем имя выходного файла

// выключим ofile_on

}

else if ( limit_on ) { // если -l встречалась

// обработаем максимальный размер

// выключим limit_on

} else {

// обработаем имя входного файла

}

}

Если аргумент является именем выходного файла, сохраним это имя и выключим ofile_on:

if ( ofile_on ) {

ofile_on = false;

ofile = pchar;

}

Если аргумент задает максимальный размер, мы должны преобразовать строку встроенного типа в представляемое ею число. Сделаем это с помощью стандартной функции atoi(), которая принимает строку в качестве аргумента и возвращает int (также существует функция atof(), возвращающая double). Для использования atoi() включим заголовочный файл ctype.h. Нужно проверить, что значение максимального размера неотрицательно и выключить limit_on:

// int limit;

else

if ( limit_on ) {

limit_on = false;

limit = atoi( pchar );

if ( limit < 0 ) {

cerr < < program_name < < ":: "

    < < program_version < < " : error: "

    < < " negative value for limit.\n\n";

usage( -2 );

}

}

Если обе переменных состояния равны false, у нас есть имя входного файла. Сохраним его в векторе строк:

 

else

file_names.push_back( string( pchar ));

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

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

prog - d dataOl

prog -oout_file dataOl

(Оба случая мы оставим для упражнений в конце раздела.)

Вот полный текст нашей программы. (Мы добавили инструкции печати для трассировки выполнения.)

#include < iostream>

 

#include < string>

#include < vector>

 

#include < ctype.h>

 

const char *const program_name = " comline";

const char *const program_version = " version 0.01 (08/07/97)";

 

inline void usage( int exit_value = 0 )

{

// печатает отформатированное сообщение о порядке вызова

// и завершает программу с кодом exit_value...

 

cerr < < " порядок вызова: \n"

  < < program_name < < " "

  < < " [-d] [-h] [-v] \n\t"

  < < " [-o output_file] [-l limit] \n\t"

  < < " file_name\n\t[file_name [file_name [... ]]]\n\n"

  < < " где [] указывает на необязательность опции: \n\n\t"

  < < " -h: справка.\n\t\t"

  < < " печать этого сообщения и выход\n\n\t"

  < < " -v: версия.\n\t\t"

  < < " печать информации о версии программы и выход\n\n\t"

  < < " -d: отладка.\n\t\t включает отладочную печать\n\n\t"

  < < " -l limit\n\t\t"

  < < " limit должен быть неотрицательным целым числом\n\n\t"

  < < " -o ofile\n\t\t"

  < < " файл, в который выводится результат\n\t\t"

  < < " по умолчанию результат записывается на стандартный вывод\n\n"

     < < " file_name\n\t\t"

  < < " имя подлежащего обработке файла\n\t\t"

  < < " должно быть задано хотя бы одно имя --\n\t\t"

  < < " но максимальное число не ограничено\n\n"

  < < " примеры: \n\t\t"

  < < " $command chapter7.doc\n\t\t"

   < < " $command -d -l 1024 -o test_7_8 "

  < < " chapter7.doc chapter8.doc\n\n";

 

exit( exit_value );

}

 

int main( int argc, char* argv[] )

{

bool debug_on = false;

bool ofile_on = false;

bool limit_on = false;

int limit = -1;

string ofile;

vector< string> file_names;

 

cout < < " демонстрация обработки параметров в командной строке: \n"

  < < " argc: " < < argc < < endl;

 

for ( int ix = 1; ix < argc; ++ix )

{

cout < < " argv[ " < < ix < < " ]: "

    < < argv[ ix ] < < endl;

 

char *pchar = argv[ ix ];

switch ( pchar[ 0 ] )

{

case '-':

{

   cout < < " встретился \'-\'\n";

   switch( pchar[ 1 ] )

   {

     case 'd':

       cout < < " встретилась -d: "

            < < " отладочная печать включена\n";

 

       debug_on = true;

       break;

 

     case 'v':

       cout < < " встретилась -v: "

            < < " выводится информация о версии\n";

 

       cout < < program_name

            < < " :: "

            < < program_version

            < < endl;

 

       return 0;

 

     case 'h':

       cout < < " встретилась -h: "

            < < " справка\n";

 

       // break не нужен: usage() завершает программу

       usage();

 

     case 'o':

       cout < < " встретилась -o: выходной файл\n";

       ofile_on = true;

       break;

     case 'l':

       cout < < " встретилась -l: "

            < < " ограничение ресурса\n";

 

       limit_on = true;

       break;

 

     default:

       cerr < < program_name

            < < " : ошибка: "

            < < " неопознанная опция: - "

            < < pchar < < " \n\n";

 

       // break не нужен: usage() завершает программу

       usage( -1 );

   }

   break;

 }

 

default: // либо имя файла

   cout < < " default: параметр без дефиса: "

        < < pchar < < endl;

 

   if ( ofile_on ) {

     ofile_on = false;

     ofile = pchar;

   }

   else

   if ( limit_on ) {

     limit_on = false;

     limit = atoi( pchar );

     if ( limit < 0 ) {

       cerr < < program_name

            < < " : ошибка: "

            < < " отрицательное значение limit.\n\n";

 

       usage( -2 );

     }

   }

     else file_names.push_back( string( pchar ));

   break;

}

}

 

if ( file_names.empty() ) {

cerr < < program_name

    < < " : ошибка: "

    < < " не задан ни один входной файл.\n\n";

usage( -3 );

}

 

if ( limit! = -1 )

cout < < " Заданное пользователем значение limit: "

    < < limit < < endl;

 

if (! ofile.empty() )

cout < < " Заданный пользователем выходной файл: "

    < < ofile < < endl;

 

cout < < (file_names.size() == 1? " Файл, " : " Файлы, " )

  < < " подлежащий(е) обработке: \n";

 

for ( int inx = 0; inx < file_names.size(); ++inx )

cout < < " \t" < < file_names[ inx ] < < endl;

}

a.out -d -l 1024 -o test_7_8 chapter7.doc chapters.doc

Вот трассировка обработки параметров командной строки:

 

демонстрация обработки параметров в командной строке:

argc: 8

argv[ 1 ]: -d

встретился '-'

встретилась -d: отладочная печать включена

argv[ 2 ]: -l

встретился '-'

встретилась -l: ограничение ресурса

argv[ 3 ]: 1024

default: параметр без дефиса: 1024

argv[ 4 ]: -o

встретился '-'

встретилась -o: выходной файл

argv[ 5 ]: test_7_8

default: параметр без дефиса: test_7_8

argv[ 6 ]: chapter7.doc

default: параметр без дефиса: chapter7.doc

argv[ 7 ]: chapter8.doc

default: параметр без дефиса: chapter8.doc

Заданное пользователем значение limit: 1024

Заданный пользователем выходной файл: test_7_8

Файлы, подлежащий(е) обработке:

   chapter7.doc

   chapter8.doc

 


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 305; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.08 с.)
Главная | Случайная страница | Обратная связь