我有一个很大的(读取:噩梦)方法,这种方法多年来一直在增长,以支持我的项目不断增长的命令行参数列表。我的意思是几页自述文档,每个参数都有简短的模糊。
当我添加了每个功能时,我只是通过在该方法中添加几行来“注册”处理该参数的方法。
然而,这种方法现在不雅观,容易出错,而且难以理解。以下是目前处理此方法的两种方法中较短的一个示例:
//All double dash arguments modify global options of the program,
//such as --all --debug --timeout etc.
void consoleParser::wordArgParse(std::vector<criterion *> *results)
{
TCHAR const *compareCurWordArg = curToken.c_str()+2;
if (!_tcsicmp(compareCurWordArg,_T("all")))
{
globalOptions::showall = TRUE;
} else if (!_tcsnicmp(compareCurWordArg,_T("custom"),6))
{
if (curToken[9] == L':')
{
globalOptions::display = curToken.substr(10,curToken.length()-11);
} else
{
globalOptions::display = curToken.substr(9,curToken.length()-10);
}
} else if (*compareCurWordArg == L'c' || *compareCurWordArg == L'C')
{
if (curToken[3] == L':')
{
globalOptions::display = curToken.substr(5,curToken.length()-6);
} else
{
globalOptions::display = curToken.substr(4,curToken.length()-5);
}
} else if (!_tcsicmp(compareCurWordArg,_T("debug")))
{
globalOptions::debug = TRUE;
} else if (!_tcsicmp(compareCurWordArg,L"expand"))
{
globalOptions::expandRegex = false;
} else if (!_tcsicmp(compareCurWordArg,L"fileLook"))
{
globalOptions::display = L"---- #f ----#nCompany: #d#nFile Description: #e#nFile Version: #g"
L"#nProduct Name: #i#nCopyright: #j#nOriginal file name: #k#nFile Size: #u#nCreated Time: #c"
L"#nModified Time: #m#nAccessed Time: #a#nMD5: #5#nSHA1: #1";
} else if (!_tcsicmp(compareCurWordArg,_T("peinfo")))
{
globalOptions::display = _T("[#p] #f");
} else if (!_tcsicmp(compareCurWordArg,L"enable-filesystem-redirector-64"))
{
globalOptions::disable64Redirector = false;
} else if (!_tcsnicmp(compareCurWordArg,_T("encoding"),8))
{
//Performance enhancement -- encoding compare only done once.
compareCurWordArg += 8;
if (!_tcsicmp(compareCurWordArg,_T("acp")))
{
globalOptions::encoding = globalOptions::ENCODING_TYPE_ACP;
} else if (!_tcsicmp(compareCurWordArg,_T("oem")))
{
globalOptions::encoding = globalOptions::ENCODING_TYPE_OEM;
} else if (!_tcsicmp(compareCurWordArg,_T("utf8")))
{
globalOptions::encoding = globalOptions::ENCODING_TYPE_UTF8;
} else if (!_tcsicmp(compareCurWordArg,_T("utf16")))
{
globalOptions::encoding = globalOptions::ENCODING_TYPE_UTF16;
} else
{
throw eMsg(L"Unrecognised encoding word argument!\r\nValid choices are --encodingACP --encodingOEM --encodingUTF8 and --encodingUTF16. Terminate.");
}
} else if (!_tcsnicmp(compareCurWordArg,L"files",5))
{
compareCurWordArg += 5;
if (*compareCurWordArg == L':') compareCurWordArg++;
std::wstring filePath(compareCurWordArg);
globalOptions::regexes.insert(globalOptions::regexes.end(), new filesRegexPlaceHolder);
results->insert(results->end(),new filesRegexPlaceHolder);
boost::algorithm::trim_if(filePath,std::bind2nd(std::equal_to<wchar_t>(),L'"'));
loadFiles(filePath);
} else if (!_tcsicmp(compareCurWordArg,_T("full")))
{
globalOptions::fullPath = TRUE;
} else if (!_tcsicmp(compareCurWordArg,_T("fs32")))
{
globalOptions::disable64Redirector = false;
} else if (!_tcsicmp(compareCurWordArg,_T("long")))
{
globalOptions::display = _T("#t #s #m #f");
globalOptions::summary = TRUE;
} else if (!_tcsnicmp(compareCurWordArg,_T("limit"),5))
{
compareCurWordArg += 5;
if (*compareCurWordArg == _T(':'))
compareCurWordArg++;
globalOptions::lineLimit = _tcstoui64(compareCurWordArg,NULL,10);
if (!globalOptions::lineLimit)
{
std::wcerr << eMsg(L"Warning: You are limiting to infinity lines. Check one of your --limit options!\r\n");
}
} else if (!_tcsicmp(compareCurWordArg,_T("short")))
{
globalOptions::display = _T("#8");
} else if (!_tcsicmp(compareCurWordArg,_T("summary")))
{
globalOptions::summary = TRUE;
} else if (!_tcsicmp(compareCurWordArg,_T("norecursion")))
{
globalOptions::noSubDirs = TRUE;
} else if (!_tcsnicmp(compareCurWordArg,_T("timeout"),7))
{
compareCurWordArg += 7;
if (*compareCurWordArg == _T(':'))
compareCurWordArg++;
globalOptions::timeout = _tcstoul(compareCurWordArg,NULL,10);
if (!globalOptions::timeout)
{
std::wcerr << eMsg(L"Warning: You are limiting to infinite time. Check one of your --timeout options!\r\n");
}
} else if (!_tcsnicmp(compareCurWordArg,_T("tx"),2))
{
compareCurWordArg += 2;
if (*compareCurWordArg == _T(':'))
compareCurWordArg++;
globalOptions::timeout = _tcstoul(compareCurWordArg,NULL,10);
if (!globalOptions::timeout)
{
std::wcerr << eMsg(L"Warning: You are limiting to infinite time. Check one of your --timeout options!\r\n");
}
} else
{
throw eMsg(L"Could not understand word argument! Ensure all of your directives are spelled correctly. Terminate.");
}
}
我发布了长篇文章,但它超过500行。
有没有更好的方法来处理这个特定的问题,还是应该把它作为一个很长的方法?
编辑:我不是在寻找一个令牌化库 - 我已经完成了那项肮脏的工作。我很好奇是否有必要从更大的脏方法中创建存根方法。Billy3
答案 0 :(得分:5)
我确信Windows的等效getopt(3)功能。 这是Google的第一个热门话题 - Pete Wilson。 或者你可以查看Boost Program Options一个体面的C ++库。
答案 1 :(得分:2)
你需要的是command line option parser library来处理处理命令行参数的混乱细节。
我不确定哪一个最适合C ++,因为我是一个使用CSharpOptParse的C#开发人员......但这个概念应该是相同的,所以希望知道要寻找什么会指向你正确的方向。
答案 2 :(得分:0)
HI全部,我写了这个小帮手,用于处理命令行。我还更新了它以使用时髦: - file'thing'并按照提问者的需要进行分割。 要使其使用其他字符类型,只需使用您正在使用的字符替换char类型。这是一个完整的工作示例,您可以粘贴到main.cpp并运行。 代码执行正确的转义,引用分组和:和='“作为args的名称/值拆分器,这样你就可以--flag:1或-file”c:\ test“。注意空间用作选项拆分器。它看起来像这样在代码中使用它:
optparse opt(argstring);
g_someint = strtoul(opt.get('--debuglevel','0'),0,0);
g_somebool = opt.get('--flag')!=0;
g_somestring = opt.get('--file','default.txt')
回答这个问题:你可以看到这使得你的论证处理代码变得如此简单,你真的不需要模块化它。它的可读性和可维护性。
#include <string.h>
#include <stdio.h>
struct optparse{
optparse(const char *args, size_t len = 0) : first(0) {
size_t i;
if(!args)args = "";
if(!len)for(;args[len];len++);
for(buf=new char[len+1],i=0;i<len;i++)buf[i]=args[i];buf[i]=0;
opt *last = first;
char *c = buf, *b = c, *v = 0, g = 0, e = 0;
do{
if(*c=='\\') e = e?0:1;
else if(e?--e:1){
if(g){ if(*c == g) g = 0; }
else {
if(*c=='"' || *c=='\''){ if(b<c && !v) v = c; g = *c; }
else if(!v && (*c==':' || *c=='=')) v = c;
else if(*c==' '){
if(b<c)last = new opt(last,&first,b,c,v);
b = c+1, v = 0;
}
}
}
if(*c) c++;
if(!*c && b<c) last = new opt(last,&first,b,c,v);
}while(*c);
for(opt *i = first; i; i = i->next) *(i->ne) = 0, *(i->ve) = 0;
}
~optparse(){
delete buf;
while(first){
opt *t = first->next;
delete first;
first = t ;
}
}
const char *get( const char *name, const char *def= 0){
size_t l = strlen(name);
for(opt *i = first;i;i = i->next) if( _strnicmp( i->name, name, l ) == 0)
return i->value;
return def;
}
struct opt{
opt( opt *last, opt **first, char *s, char *e, char *v){
if(!*first) *first = this; if(last) last->next = this;
if(v && (*v=='\'' || *v=='"') && (*(e-1)=='\'' || *(e-1) == '"'))e--;
next = 0, name = s, value = v?v+1:"", ne = v?v:e, ve = e;
}
char *name, *value, *ne, *ve;
opt *next;
};
char *buf;
opt *first;
};
int main(){
const char *v, *test ="--debug:1 -file'c:\\something' --odd=10";
optparse opts(test);
if(v = opts.get("--debug")){
printf("debug flag value is %s\n",v);
}
for(optparse::opt *i=opts.first;i;i=i->next){
printf("name: %s value: %s\n",i->name,i->value);
}
}
非常容易调整解析器以支持不同类型的参数处理。 例如,如果你替换
if(b<c)last = new opt(last,&first,b,c,v);
b = c+1, v = 0;
与
if(*b=='-' && *(c+1)!='-')v = v?v:c;
else{
if(b<c)last = new opt(last,&first,b,c,v);
b = c+1, v = 0;
}
您将添加将空格分割参数连接为“值”的功能,如:-debug 1 或--files a.txt b.txt c.txt 此外,如果您不喜欢:作为一个拆分参数(在Windows应用程序中可能很麻烦),只需删除==':'