Сделай Сам Свою Работу на 5

Функция 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

 

 








Не нашли, что искали? Воспользуйтесь поиском по сайту:



©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.