命令行解析代码

来源:互联网 发布:春秋战国士阶层 知乎 编辑:程序博客网 时间:2024/05/01 10:25

开发设计过程中,往往要对命令行进行解析,下面实现了命令行解析功能,具体见代码(QuickHash在之前文章中),欢迎讨论:

命令选项类,封装-p等命令选项

#ifndef COMMAND_OPTION_H#define COMMAND_OPTION_H#include <string>/**@file CommandOption.h * * @author xiaoxing.zhou * @date 2012.4.28 * @version v1.0 *//**@class ParamBase * 参数值基类. * 必须参数直接使用 */class ParamBase{public:ParamBase(){};ParamBase(const char* pStr){if(pStr)m_value=pStr;}void SetValue(const char* pStr){if(pStr)m_value=pStr;}/**获取参数值*/char GetChar();int GetInt();bool GetBool();const char* GetStr();float GetFloat();protected:std::string m_value;};/**@class CommandOption * 选项参数类 */class CommandOption:public ParamBase{public:/**@enum * 选项参数带值类型 */enum ValueType{NON_VALUE,//!<不带值DEFAULT_VALUE,//!<可设置默认NEED_VALUE//!必须带值};/**构造函数*/CommandOption(char option, const char* pName=NULL);/**设置可选*/void SetNFlag(bool flag=true){m_nflag=flag;m_sflag=false;}bool GetNFlag(){return m_nflag;}void DoSet(){m_sflag=true;}bool IsSet(){return m_sflag;}//!判定可选参数是否设置/**设置带值属性*/void SetVFlag(ValueType flag=NON_VALUE){m_vflag=flag;}ValueType GetVFlag(){return m_vflag;}/**设置参数值*/bool SetValue(const char* pValue);/**获取短选项名称*/char GetOption(){return m_option;}/**获取长选项名称*/const char* GetName(){if(m_name.length())return m_name.c_str();else return m_name.c_str();}private:bool m_nflag;//!<可选标识bool m_sflag;//!<是否设置ValueType m_vflag;char m_option;//!<短选项std::string m_name;//!<长选项};#endif
#include "CommandOption.h"char ParamBase::GetChar(){if(m_value.length()){return m_value[0];}return '\0';}int ParamBase::GetInt(){if(m_value.length()){return atoi(m_value.c_str());}return 0;}bool ParamBase::GetBool(){if(m_value.length()){return m_value.compare("true")?true:false;}return false;}const char* ParamBase::GetStr(){if(m_value.length()){return m_value.c_str();}return NULL;}float ParamBase::GetFloat(){if(m_value.length()){return atof(m_value.c_str());}return 0.0;}CommandOption::CommandOption(char option, const char* pName){m_option=option;if(pName)m_name=pName;SetNFlag();SetVFlag();}bool CommandOption::SetValue(const char* pValue){switch(m_vflag){case NON_VALUE:return false;case DEFAULT_VALUE:if(pValue){ParamBase::SetValue(pValue);}return true;case NEED_VALUE:if(pValue){ParamBase::SetValue(pValue);return true;}return false;default:return false;}}
命令行解析实现,解释见注释。
#ifndef COMMAND_LINE_H#define COMMAND_LINE_H#include <string>#include <vector>#include "CommandOption.h"#include "QuickHash.h"/**@file CommandLine.h * 定义命令行类. * 命令行参数分为选项参数和必须参数: * 1)选项参数以-o短选项或者--option出现; *     (1)按照是否必须,选项参数分为必须和可选二类 *     (2)按照是否带值选项参数,选项参数分为不带值,可设置默认值和带值三类 *     (3)选项参数没有顺序要求,除要求可设置默认值的选项参数不能与必须参数相邻(无法区分带的值归属)外 *     (4) 允许无参数的短选项合并使用 * 2)必须参数即跟在选项参数后面,没有特殊要求。 * @author xiaoxing.zhou * @date 2012.4.28 * @version v1.0 *//**@class CommandLine * 命令行类. * 命令行的形式变化多样,为了规范命令行的解析,需要定义描述命令行参数形式的模式,以便于命令行对象解析 * 命令行模式:={%[option]}$$... * {%[option]}:选项参数可以多个 * %表明为选项参数 * option:=[?](f|[f|file])[:|?=value] * ?:可选的选项参数,否则为必须选项参数 * f:选项参数短选项 * [f|file]:选项参数短和长选项 * ::带参数的选项参数 * ?=value:可设置默认值的选项参数,value为默认值 * $$...:必须参数必须在选项参数后面 * eg. --portnum 3000 -C confg.cfg train.list * pattern:%[p|portnum]:%C:$ */class CommandLine{public:/**构造命令行对象*/CommandLine(const char* pCommandLine);CommandLine(int argc, const char* argv[]);~CommandLine();/**解析方法*/bool Parse(const char* pCommandPattern);/**获取选项参数*/CommandOption* GetOpt(const char* pName){if(!pName)return NULL;SHashTable::Node* pNode=m_LOptHash.Find(pName);if(!pNode)return NULL;return pNode->GetValue();}CommandOption* GetOpt(char name){CHashTable::Node* pNode=m_OptHash.Find(name);if(!pNode)return NULL;return pNode->GetValue();}/**获取必须参数*/ParamBase* GetParam(int index=0){if(index<0||index>=m_ParamVect.size())return NULL;return m_ParamVect[index];}protected:/**解析一个选项*/CommandOption* Parse(const char* pBegin, const char* pEnd);private:/**保存输入参数*/std::vector<std::string> m_argv;/**快速查找*/typedef QuickHash<int,CommandOption*,Equal,Hash,100> CHashTable;typedef QuickHash<const char*, CommandOption*, Equal, Hash,100> SHashTable;CHashTable m_OptHash;SHashTable m_LOptHash;/**选项参数容器*/std::vector<CommandOption*> m_OptVect;/**必须参数容器*/std::vector<ParamBase*> m_ParamVect;};#endif

#include "CommandLine.h"CommandLine::CommandLine(int argc, const char* argv[]){for(int i=0; i<argc; ++i){m_argv.push_back(argv[i]);}}CommandLine::CommandLine(const char* pCommandLine){if(pCommandLine){const char* pIndex=pCommandLine;const char* pBegin=pIndex;/**过滤空格*/while(*pIndex&&*pIndex==' ')++pIndex;std::string temp;while(*pIndex){if(*pIndex==' '&&(pBegin<pIndex)){temp.assign(pBegin,pIndex);m_argv.push_back(temp.c_str());while(*pIndex&&*pIndex==' ')++pIndex;pBegin=pIndex;}else if(*pIndex=='"'){pBegin=pIndex;/**找到匹配的'"'*/while(*pIndex&&*pIndex!='"')++pIndex;if(*pIndex){temp.assign(pBegin,pIndex++);m_argv.push_back(temp.c_str());}else return;while(*pIndex&&*pIndex==' ')++pIndex;pBegin=pIndex;}else ++pIndex;}}}CommandLine::~CommandLine(){for(int i=0; i<m_OptVect.size();++i){delete m_OptVect[i];}for(int i=0; i<m_ParamVect.size(); ++i){delete m_ParamVect[i];}}bool CommandLine::Parse(const char* pCommandPattern){/**首先解析模式*/if(!pCommandPattern)return false;const char* pCur=pCommandPattern;const char* pNext;CommandOption* pOpt;int paramCount=0;while(*pCur){if(*pCur=='%'){pNext=strchr(pCur+1,'%');if(!pNext)pNext=strchr(pCur,'$');if(!pNext)pNext=pCur+strlen(pCur);if(pNext){if(!(pOpt=Parse(pCur,pNext))){return false;}m_OptVect.push_back(pOpt);/**存入HashTable中*/m_OptHash.Insert(pOpt->GetOption(),pOpt);if(pOpt->GetName())m_LOptHash.Insert(pOpt->GetName(),pOpt);}pCur=pNext;}else if(*pCur=='$'){m_ParamVect.push_back(new ParamBase());//++paramCount;++pCur;}else return false;}/**解析命令行*/int i=0;int ncount=0;for(i=0; i<m_OptVect.size(); ++i){if(m_OptVect[i]->GetNFlag())++ncount;}i=0;int argc=m_argv.size();while(i<argc){if(m_argv[i][0]=='-'){if(m_argv[i][1]=='-'){pOpt=GetOpt(m_argv[i].c_str()+2);}else if(m_argv[i].length()>2){//短选项合并for(int k=1; k<m_argv[i].length(); ++k){if(!(pOpt=GetOpt(m_argv[i][k])))return false;if(pOpt->GetVFlag()!=CommandOption::NON_VALUE)return false;if(pOpt->GetNFlag())--ncount;else{pOpt->DoSet();//!表明可选项出现}}++i;continue;}else pOpt=GetOpt(m_argv[i][1]);if(!pOpt)return false;//!出现未定义的选项if(pOpt->GetNFlag())--ncount;//!必须选项出现的次数else{pOpt->DoSet();}switch(pOpt->GetVFlag()){case CommandOption::NON_VALUE:++i;break;case CommandOption::DEFAULT_VALUE:if((++i)<argc&&m_argv[i][0]!='-'){//!值默认选项不能出现在最后if(!pOpt->SetValue(m_argv[i++].c_str()))return false;}break;case CommandOption::NEED_VALUE:if((++i)<argc&&m_argv[i][0]!='_'){if(!pOpt->SetValue(m_argv[i++].c_str()))return false;}else return false;break;default:return false;}}else{if(argc-i!=m_ParamVect.size())return false;paramCount=m_ParamVect.size();int j=0;for(; i<argc; ++i){m_ParamVect[j++]->SetValue(m_argv[i].c_str());}}}if(m_ParamVect.size()!=paramCount||ncount>0)return false;return true;}/**%[?](f|[f|file])(:|?=value)*/CommandOption* CommandLine::Parse(const char* pBegin, const char* pEnd){/**至少两个字节*/if(pEnd-2<pBegin||*(pBegin++)!='%')return NULL;char opt;std::string name;bool nflag=true;if(*pBegin=='?'&&(++pBegin<pEnd)){nflag=false;}if(*pBegin=='['){const char* pTemp=pBegin;while(pTemp<pEnd){if(*pTemp!=']')++pTemp;else break;}if(pTemp>=pEnd||pTemp-5<pBegin||*(pBegin+2)!='|')return NULL;++pBegin;opt=*(pBegin++);name.assign(++pBegin,pTemp);pBegin=pTemp+1;}else{opt=*(pBegin++);}CommandOption* pOpt;if(name.length())pOpt=new CommandOption(opt,name.c_str());else pOpt=new CommandOption(opt);pOpt->SetNFlag(nflag);if(pBegin<pEnd){switch(*pBegin){case ':':pOpt->SetVFlag(CommandOption::NEED_VALUE);break;case '?':if(pBegin+2>pEnd){delete pOpt;return NULL;}else{std::string value(pBegin+2,pEnd);pOpt->SetVFlag(CommandOption::DEFAULT_VALUE);pOpt->SetValue(value.c_str());}break;default:;}}return pOpt;}
#include "CommandLine.h"bool TestCase1(int argc, const char* argv[]){CommandLine command(argc-1, argv+1);if(!command.Parse("%[P|portnum]?=30000%C:$$")){printf("input incorrect\n");return false;}printf("-P %d -C %s %s %s\n",command.GetOpt('P')->GetInt(),command.GetOpt('C')->GetStr(),command.GetParam(0)->GetStr(),command.GetParam(1)->GetStr());return true;}bool TestCase2(const char* pStr1){CommandLine command(pStr1);if(command.Parse("%?d%m%?P?=2000%C:%D?=true%M?=true")){printf("-P %d -C %s -D %s -M %s\n",command.GetOpt('P')->GetInt(),command.GetOpt('C')->GetStr(),command.GetOpt('D')->GetStr(),command.GetOpt('M')->GetStr());return true;}else{printf("input incorrect\n");return false;}}int main(int argc, const char* argv[]){TestCase1(argc, argv);TestCase2("-dm -C server.conf -D true -M true");return 0;}

测试代码,

TestCase1测试的命令行模式是 -P|portnum [30000] -C <config> param0 param1

意思是,指定端口,如果没有指定,默认30000,并且要求提供配置文件,附带两个参数

TestCase2测试的是 [-d] -m [-P [2000]] -C <value> -D [true] -M [true] 

[]的意思表示可以提供,也可以不提供。




1 0