命令行解析代码

开发设计过程中,往往要对命令行进行解析,下面实现了命令行解析功能,具体见代码(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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值