程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 命令行參數解析的一些心得

命令行參數解析的一些心得

編輯:關於C語言

 

解析命令行參數,這個看起來很簡單,但是其中有不少小問題,在這裡我先記錄下這幾天coding的一些心得體會,和大家共同探討一下。

首先明確需求。

要解析命令行參數,最起碼得有輸入輸出吧。

輸入一般有這麼幾種:

1.跟在可執行文件後的一長串參數選項,表達式

2.配置文件

3.標准輸入,這個可以由其他程序的輸出通過管道重定向而來。

輸出:

這個就比較單一了,要麼屏幕,要麼文件。

看起來輸入比較麻煩,那就主要分析一下輸入吧。上面這三種輸入方式其實都可以通過參數選項進行控制,所以進而歸類到如何解析參數選項和表達式呢。

我選用了boost::program_options和boost::property_tree兩個庫來幫忙。

這兩者的設計目的截然不同,program_options主要是用來parse入口參數,它的存儲介質是variable_map,這是個std::map的繼承版本。本質上也是鍵值有序對,所以很顯然,variable_map的層次結構只有一層。

而property_tree的設計目的是用來存儲層次結構的數據,所以裡面有路徑這一說法,但是我覺得很奇怪,為啥program_options不使用property_tree作為存儲介質呢?

借助property_tree解析xml,json,ini,info的能力,program_options可以很方便的存儲和讀取配置文件啊。

所以我決定把兩者拼起來。

首先設計一個基類:

 1 class parser_cmd_line

 2         {

 3         public:

 4                 /**

 5                  * Parse the main parameters. It depends on the init_option_data and special_parser function \n

 6                  *

 7                  * @param argc arguments number from

 8                  * @param argv arguments from main

 9                  */

10                 void parser(int argc, char *argv[]);

11                 virtual ~parser_cmd_line(){}

12         protected:

13                 /**

14                  * This function init the data_, which needs to be rewrote, it includes sereral steps: \n

15                  * 1. Add option term with long and short term. \n

16                  * 2. Add option description and option handler. \n

17                  * 3. Both terms above are pair format <long term, short term>, <description, handler>

18                  * 4. The handler uses boost::function, which should bind to the function you need.

19                  */

20                 virtual void init_option_data(){}

21                 /**

22                  * Add a user designed parser, you can parse some special style input as the "style_" offered.

23                  * It also needs to be rewrote.

24                  * @param argc

25                  * @param argv

26                  */

27                 virtual void special_parser(int argc, char *argv[], const char * style[] = style_){}

28

29                 typedef function<void(const std::string&)> fPtr;

30                 vector<tuple<string,string,string,fPtr> > data_;

31                 static const char * style_[];

32                 options_description opts;

33             };

 

  parser是對外的接口,負責解析命令行參數,init_option_data和special_parser 留給用戶實現,主要是用來初始化一些內建的選項,和對特定形式的表達式進行解析。往往得針對每個選項,都會有些特定的操作,所以我在此使用了boost::function庫,它可以很方便的綁定函數,傳遞變量。但是有利也有弊端,使用boost::function帶來的一個問題是,我無法在map中建立<key,function>這樣的有序對,因為map本質上是一個同源容器,它無法存儲不同類型的數據。(參見http://stackoverflow.com/questions/646737/map-of-boost-function-of-different-types)所以我只能換tuple了,這也是boost中的一個庫,有點類似范化的pair,可以比pair容納更多元素。

有了這些定義,下面實現parser就變得很簡單了。program-option還有個缺陷,即假如輸入為定義的選項時會丟出一個異常,其實這往往不是用戶想要的,比較好的方式是給出一個提示,這就是第21-27行干的事。

 1 void parser_cmd_line::parser(int argc, char * argv[])

 2   {

 3     init_option_data();

 4

 5     typedef vector<tuple<string,string,string,fPtr> >::const_iterator vci;

 6     for(vci it = data_.begin(); it != data_.end(); ++it)

 7       {

 8         std::string option_name = it->get<0>();

 9         if(!it->get<1>().empty())

10           {

11             option_name += ",";

12             option_name += it->get<1>();

13           }

14         const string &desc = it->get<2>();

15         opts.add_options()(option_name.c_str(), desc.c_str());

16       }

17

18     special_parser(argc,argv,style_);

19    

20     variables_map vm;

21     BOOST_AUTO(pr, command_line_parser(argc,argv).options(opts).allow_unregistered().run());

22     vector<string> ur_opts = collect_unrecognized(pr.options, include_positional);

23     BOOST_FOREACH(string str, ur_opts)

24       {

25         if(str.find(style_[1]) == std::string::npos)

26           std::cerr << "Unknown option: " << str << std::endl;

27       }

28     store(pr,vm);

29     notify(vm);

30    

31     if(vm.size() == 0 && argc == 1)

32       {

33         std::cout << opts << std::endl;

34         return ;

35       }

36

37     for(vci it = data_.begin();    it != data_.end(); ++it)

38       {

39         const std::string& key = it->get<0>();

40         if(vm.count(key.c_str()))

41            it->get<3>()(key);

42       }

43    

44   }

 

用戶可以繼承這個基類後,添加選項,描述,想對應的處理函數等等。

等等,你是不是發現啥問題了,對,按照我這樣的寫法,你是無法實現-L./xxx.so這樣的功能的。我並沒有提供一個參數後跟一個輸入的形式,主要原因是我覺得這樣的例子並不直觀,我更傾向於L=xx.so這樣的表達式,所以我提供了special_parser這個函數,你可以很方便的擴展成你想要的style。

當然現在的這個功能還是很單一的,比如因為我的handler是以此調用,所以假如你的選項之間有依賴關系的話,在添加時就得格外小心了。

暫時就這些,肯定還有很多理解不到的地方,請大家多多指教阿。

閒人草堂

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved