2009-05-20 15 views
3

私のプロジェクトの絶えず増大するコマンドライン引数のリストをサポートするために、長年にわたって成長してきた大規模なメソッド(読み込み:悪夢)があります。議論ごとに短いblurbsのためのreadmeドキュメントのいくつかのページを意味します。長いコマンドラインテストメソッドを分割する

私は各機能を追加したので、そのメソッドに数行追加するだけでその引数を処理する方法を単に「登録」しました。

しかし、その方法は見苦しく、バグが発生しやすく、理解しにくいです。ここでは、現在これを処理している2つの方法のうち短い方の例を示します。

//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

答えて

5

私はWindows用のgetoptの同等の(3)の機能があることを確信しています。 Googleからの最初のヒット - Pete Wilsonです。 まともなC++ライブラリの場合は、Boost Program Optionsを調べることができます。

+0

ブーストライブラリ引数? [-files "C:\ Documents and Settings \ User \ Desktop \ InFile.txt"]トークンが引用符で囲まれていないこと、およびスペースが含まれていることに注意してください。引用符はトークン自体の中で始まります。 –

+0

それを試してみてください、それは本当に使いやすいです。 –

+0

試してみましたが、私の現在のシナリオでは動作しません。プロジェクトは、コマンドラインに再帰的降下パーサーを実装します。例: '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" 引用符は非標準的な方法で処理され、1つのトークンとして扱われます。 私の質問は構造的なものだと思います。 –

0

こんにちは、私はコマンドラインでの作業のためにこの小さなヘルパーを書いていました。私はまた、ファンキーで動作するように更新しました。--file'thing 'と質問者の必要に応じて分割します。 別の文字タイプを使用するには、使用している文字タイプを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; 

を交換する場合は、LIKE '値' として宇宙分割引数に参加する機能を追加しているでしょう:-debug 1 または--files A.TXTをb.txt c。txt また、:(Windowsアプリケーションでは面倒かもしれませんが)split paramとして嫌いであれば、== ':'を削除してください。

関連する問題