开发设计过程中,往往要对命令行进行解析,下面实现了命令行解析功能,具体见代码(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]
[]的意思表示可以提供,也可以不提供。