2009-05-20 2 views
3

필자의 프로젝트가 계속 증가하는 커맨드 라인 인수 목록을 지원하기 위해 수 년 동안 커져온 커다란 (악몽 읽기) 메소드가있다. 나는 인수 당 짧은 blurbs에 대한 readme 문서의 여러 페이지를 의미합니다.긴 명령 행 테스트 메소드 분리하기

각 기능을 추가 했으므로 필자는 해당 방법에 몇 줄을 추가하여 해당 인수를 처리하는 방법을 간단하게 "등록"했습니다.

그러나이 방법은보기 흉하고 버그가 발생하기 쉽고 이해하기 어렵습니다. 다음은 현재 이것을 처리하는 두 가지 메소드 중 짧은 것의 예입니다.

//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 라인을 넘습니다.

이 특정 문제를 해결하는 더 좋은 방법이 있습니까? 아니면 그냥 긴 방법으로 남겨 두어야합니까?

EDIT : 토큰 화 라이브러리를 찾고 있지 않습니다. 이미 더러운 작업을 수행했습니다. 더 큰 더러운 메서드에서 스텁 메서드를 만드는 것이 의미가 있을지 궁금합니다.

Billy3는

답변

5

나는 Windows 용 getopt는 (3) 함수의 동등한가 있음을 확신합니다. Google의 첫 번째 조회수는 Pete Wilson입니다. 또는 Boost Program Options을 참고하여 적절한 C++ 라이브러리를 구할 수 있습니다.

+0

부스트 라이브러리 논의? [-files "C : \ Documents and Settings \ User \ Desktop \ InFile.txt"] 토큰이 인용되지 않고 공백을 포함하는지 확인합니다. 따옴표는 토큰 자체에서 시작됩니다. –

+0

사용해보기, 정말 사용하기 간단합니다. –

+0

시도해보십시오. 현재 시나리오에서는 작동하지 않습니다. Project는 명령 줄에 재귀 적 파서를 구현합니다. 예 : 'progname C : \ Windows \ * AND -tf OR * .dll OR * .exe'는 파일 (폴더가 아님)이거나 .dll 또는 .exe 인 모든 파일을 인쇄합니다. 부스트 라이브러리는 쌍으로 제공되는 옵션을 중심으로 설계되었습니다. 죄송 해요. –

2

명령 줄 인수 처리의 지저분한 세부 사항을 처리하기 위해 command line option parser library이 필요합니다.

CSharpOptParse을 사용하는 C# 개발자이기 때문에 어느 것이 C++에 가장 좋을지 모르겠지만 개념은 동일해야하므로 무엇을 찾아야 올바른 방향으로 당신을 가리킬 지 알 것입니다 .

+0

내 응용 프로그램이 꽤 기괴한 명령 줄을 지원하기 때문에 그렇게 할 수 있는지 확신 할 수 없습니다. 예를 들어, 이것은 완벽하게 유효합니다. -files "C : \ Documents and Settings \ User \ Desktop \ InFile.txt" 따옴표는 비표준 방식으로 처리되고 하나의 토큰으로 처리됩니다. 제 질문은 구조적인 문제에 더 가깝습니다. –

0

안녕 모두, 나는 명령 줄 작업을 위해이 작은 도우미를 썼다. 나는 또한 펑키와 함께 작동하도록 업데이트했다. 다른 문자 유형을 사용하려면 char 유형을 사용중인 것으로 대체하십시오. 이것은 main.cpp에 붙여 넣고 실행할 수있는 완전한 예제입니다. args에 대한 이름/값 분리 자로 적절한 이스케이프, quotegrouping 및 : and = ' "을 할 수 있으므로 --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+1)!='-')v = v?v:c; 
else{ 
    if(b<c)last = new opt(last,&first,b,c,v); 
    b = c+1, v = 0; 
} 
당신이 좋아하는 '가치'로 공간을 분할 인수를 결합의 기능을 추가 한 것입니다

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 또한 다음을 싫어하는 경우 : split param (Windows 응용 프로그램에서 불편할 수 있음)은 == ':'을 제거하십시오.