С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 58
Текст из файла (страница 58)
Мы используем для этого цикл for сfor ( int ix = 1; ix < argc; ++ix ) {char *pchar = argv[ ix ];// ...начальным индексом 1 (пропуская, таким образом, имя программы):}2. Определить тип параметра. Если строка начинается с дефиса (-), это одна из опций {h, d, v, l, o}. В противном случае это может быть либо значение, ассоциированное сопцией (максимальный размер для -l, имя выходного файла для -o), либо имявходного файла. Чтобы определить, начинается ли строка с дефиса, используемswitch ( pchar[ 0 ] ) {case '-': {// -h, -d, -v, -l, -o}default: {// обработаем максимальный размер для опции -1//имя выходного файла для-o//имена входных файлов ...}инструкцию switch:351С++ для начинающих}Реализуем обработку двух случаев пункта 2.Если строка начинается с дефиса, мы используем switch по следующему символу дляcase '-': {switch( pchar[ 1 ] ){case 'd':// обработка опции debugbreak;case 'v':// обработка опции versionbreak;case 'h':// обработка опции helpbreak;case 'o':// приготовимся обработать выходной файлbreak;case 'l':// приготовимся обработать макс.размерbreak;default:// неопознанная опция:// сообщить об ошибке и завершить выполнение}определения конкретной опции.
Вот общая схема этой части программы:}Опция -d задает необходимость отладки. Ее обработка заключается в присваиваниипеременной с объявлениемbool debug_on = false;case 'd':debug_on = true;значения true:break;if ( debug_on )В нашу программу может входить код следующего вида:display_state_elements( obj );352С++ для начинающихcase 'v':cout << program_name << "::"<< program_version << endl;Опция -v выводит номер версии программы и завершает исполнение:return 0;Опция -h запрашивает информацию о синтаксисе запуска и завершает исполнение.case 'h':// break не нужен: usage() вызывает exit()Вывод сообщения и выход из программы выполняется функцией usage():usage();Опция -o сигнализирует о том, что следующая строка содержит имя выходного файла.Аналогично опция -l говорит, что за ней указан максимальный размер.
Как намобработать эти ситуации?Если в строке параметра нет дефиса, возможны три варианта: параметр содержит имявыходного файла, максимальный размер или имя входного файла. Чтобы различать эти// если ofi1e_on==true,// следующий параметр - имя выходного файлаbool ofi1e_on = false;// если ofi1e_on==true,// следующий параметр - максимальный размерслучаи, присвоим true переменным, отражающим внутреннее состояние:bool limit_on = false;case 'l':limit_on = true;break;case 'o':ofile_on = true;Вот обработка опций -l и -o в нашей инструкции switch:break;Встретив строку, не начинающуюся с дефиса, мы с помощью переменных состоянияможем узнать ее содержание:353С++ для начинающих// обработаем максимальный размер для опции -1//имя выходного файла для-o//имена входных файлов ...default: {// ofile_on включена, если -o встречаласьif ( ofile_on ) {// обработаем имя выходного файла// выключим ofile_on}else if ( limit_on ) { // если -l встречалась// обработаем максимальный размер// выключим limit_on} else {// обработаем имя входного файла}}Если аргумент является именем выходного файла, сохраним это имя и выключимif ( ofile_on ) {ofile_on = false;ofile = pchar;ofile_on:}Если аргумент задает максимальный размер, мы должны преобразовать строкувстроенного типа в представляемое ею число.
Сделаем это с помощью стандартнойфункции atoi(), которая принимает строку в качестве аргумента и возвращает int(также существует функция atof(), возвращающая double). Для использования atoi()включим заголовочный файл ctype.h. Нужно проверить, что значение максимального// int limit;elseif ( 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 );}размера неотрицательно и выключить limit_on:}Если обе переменных состояния равны false, у нас есть имя входного файла. Сохранимelseего в векторе строк:file_names.push_back( string( pchar ));354С++ для начинающихПри обработке параметров командной строки важен способ реакции на неверные опции.Мы решили, что задание отрицательной величины в качестве максимального размерабудет фатальной ошибкой. Это приемлемо или нет в зависимости от ситуации.
Такжеможно распознать эту ситуацию как ошибочную, выдать предупреждение и использоватьноль или какое-либо другое значение по умолчанию.Слабость нашей реализации становится понятной, если пользователь небрежно относитсяк пробелам, разделяющим параметры. Скажем, ни одна из следующих двух строк неprog - d dataOlбудет обработана:prog -oout_file dataOl(Оба случая мы оставим для упражнений в конце раздела.)Вот полный текст нашей программы. (Мы добавили инструкции печати для трассировкивыполнения.)355#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: "356С++ для начинающих357}a.out -d -l 1024 -o test_7_8 chapter7.docchapters.docВот трассировка обработки параметров командной строки:демонстрация обработки параметров в командной строке:argc: 8argv[ 1 ]: -dвстретился '-'встретилась -d: отладочная печать включенаargv[ 2 ]: -lвстретился '-'встретилась -l: ограничение ресурсаargv[ 3 ]: 1024default: параметр без дефиса: 1024argv[ 4 ]: -oвстретился '-'встретилась -o: выходной файлargv[ 5 ]: test_7_8default: параметр без дефиса: test_7_8argv[ 6 ]: chapter7.docdefault: параметр без дефиса: chapter7.docargv[ 7 ]: chapter8.docdefault: параметр без дефиса: chapter8.docЗаданное пользователем значение limit: 1024Заданный пользователем выходной файл: test_7_8Файлы, подлежащий(е) обработке:chapter7.docchapter8.doc7.8.1.
Класс для обработки параметров командной строкиЧтобы не перегружать функцию main() деталями, касающимися обработки параметровкомандной строки, лучше отделить этот фрагмент. Можно написать для этого функцию.extern int parse_options( int arg_count, char *arg_vector );int main( int argc, char *argv[] ) {// ...int option_status;option_status = parse_options( argc, argv );// ...Например:}Как вернуть несколько значений? Обычно для этого используются глобальные объекты,которые не передаются ни в функцию для их обработки, ни обратно.
Альтернативнойстратегией является инкапсуляция обработки параметров командной строки в класс.Данные-члены класса представляют собой параметры, заданные пользователем вкомандной строке. Набор открытых встроенных функций-членов позволяет получать ихзначения. Конструктор инициализирует параметры значениями по умолчанию. Функциячлен получает argc и argv в качестве аргументов и обрабатывает их:С++ для начинающих358#include <vector>#include <string>class CommandOpt {public:CommandOpt() : _limit( -1 ), _debug_on( false ) {}int parse_options( int argc, char *argv[] );string out_file() { return _out_file; }booldebug_on() { return _debug_on; }intfiles(){ return _file_names.size(); }string& operator[]( int ix );private:inline void usage( int exit_value = 0 );bool _debug_on;int _limit;string _out_file;vector<string> _file_names;static const char *const program_name;static const char *const program_version;};#include "CommandOpt.h"int main( int argc, char "argv[] ) {// ...CommandOpt com_opt;int option_status;opttion_status = com_opt.
parse_options (argc, argv);// ...Так выглядит модифицированная функция main():18}Упражнение 7.15Добавьте обработку опций -t (включение таймера) и -b (задание размера буфераbufsize). Не забудьте обновить usage(). Например:prog -t -b 512 dataOУпражнение 7.16Наша реализация не обрабатывает случая, когда между опцией и ассоциированным с нейзначением нет пробела. Модифицируйте программу для поддержки такой обработки.Упражнение 7.17Наша реализация не может различить лишний пробел между дефисом и опцией:18 Полный текст реализации класса CommandOptиздательства Addison-Wesley.можно найти на Web-сайтеС++ для начинающихprog - d dataOМодифицируйте программу так, чтобы она распознавала подобную ошибку и сообщала оней.Упражнение 7.18В нашей программе не предусмотрен случай, когда опции -l или -o задаются несколькораз. Реализуйте такую возможность. Какова должна быть стратегия при разрешенииконфликта?Упражнение 7.19В нашей реализации задание неизвестной опции приводит к фатальной ошибке.
Как выдумаете, это оправдано? Предложите другое поведение.Упражнение 7.20Добавьте поддержку опций, начинающихся со знака плюс (+), обеспечив обработку +s и+pt, а также +sp и +ps. Предположим, что +s включает строгую проверку синтаксиса, а+p допускает использование устаревших конструкций.
Например:prog +s +p -d -b 1024 dataO7.9. Указатели на функцииПредположим, что нам нужно написать функцию сортировки, вызов которой выглядиттак:sort( start, end, compare );где start и end являются указателями на элементы массива строк. Функция sort()сортирует элементы между start и end, а аргумент compare задает операцию сравнениядвух строк этого массива.Какую реализацию выбрать для compare? Мы можем сортировать строкилексикографически, т.е. в том порядке, в котором слова располагаются в словаре, или подлине – более короткие идут раньше более длинных.
Нам нужен механизм для заданияальтернативных операций сравнения.(Заметим, что в главе 12 описан алгоритм sort() и другие обобщенные алгоритмы изстандартной библиотеки С++. В этом разделе мы покажем свою собственную версиюsort() как пример употребления указателей на функции. Наша функция будетупрощенным вариантом стандартного алгоритма.)Один из способов удовлетворить наши потребности – использовать в качестве третьегоаргумента compare указатель на функцию, применяемую для сравнения.Для того чтобы упростить использование функции sort(), не жертвуя гибкостью, можнозадать операцию сравнению по умолчанию, подходящую для большинства случаев.Предположим, что чаще всего нам требуется лексикографическая сортировка, поэтому вкачестве такой операции возьмем функцию compare() для строк (эта функция впервыевстретилась в разделе 6.10).359С++ для начинающих7.9.1.