プログラムが次のように実行されるように指定されている場合、C++ でコマンドライン引数を解析する最良の方法は何ですか?
prog [-abc] [input [output]]
標準ライブラリに組み込まれているこれを行う方法はありますか、それとも独自のコードを記述する必要がありますか?
関連している:
プログラムが次のように実行されるように指定されている場合、C++ でコマンドライン引数を解析する最良の方法は何ですか?
prog [-abc] [input [output]]
標準ライブラリに組み込まれているこれを行う方法はありますか、それとも独自のコードを記述する必要がありますか?
関連している:
Boost.Program_options はトリックを行う必要があります
テンプレート化された C++ コマンド ライン パーサー ライブラリ( GitHub のいくつかのフォークが利用可能)を提案できます。API は非常に簡単で、(サイトから引用):
ライブラリは完全にヘッダー ファイルに実装されているため、使いやすく、他のソフトウェアと一緒に配布することもできます。安心して配布できるように、MIT ライセンスの下でライセンスされています。
これはマニュアルの例で、わかりやすくするためにここに色を付けています。
#include <string>
#include <iostream>
#include <algorithm>
#include <tclap/CmdLine.h>
int main(int argc, char** argv)
{
// Wrap everything in a try block. Do this every time,
// because exceptions will be thrown for problems.
try {
// Define the command line object, and insert a message
// that describes the program. The "Command description message"
// is printed last in the help text. The second argument is the
// delimiter (usually space) and the last one is the version number.
// The CmdLine object parses the argv array based on the Arg objects
// that it contains.
TCLAP::CmdLine cmd("Command description message", ' ', "0.9");
// Define a value argument and add it to the command line.
// A value arg defines a flag and a type of value that it expects,
// such as "-n Bishop".
TCLAP::ValueArg<std::string> nameArg("n","name","Name to print",true,"homer","string");
// Add the argument nameArg to the CmdLine object. The CmdLine object
// uses this Arg to parse the command line.
cmd.add( nameArg );
// Define a switch and add it to the command line.
// A switch arg is a boolean argument and only defines a flag that
// indicates true or false. In this example the SwitchArg adds itself
// to the CmdLine object as part of the constructor. This eliminates
// the need to call the cmd.add() method. All args have support in
// their constructors to add themselves directly to the CmdLine object.
// It doesn't matter which idiom you choose, they accomplish the same thing.
TCLAP::SwitchArg reverseSwitch("r","reverse","Print name backwards", cmd, false);
// Parse the argv array.
cmd.parse( argc, argv );
// Get the value parsed by each arg.
std::string name = nameArg.getValue();
bool reverseName = reverseSwitch.getValue();
// Do what you intend.
if ( reverseName )
{
std::reverse(name.begin(),name.end());
std::cout << "My name (spelled backwards) is: " << name << std::endl;
}
else
std::cout << "My name is: " << name << std::endl;
} catch (TCLAP::ArgException &e) // catch any exceptions
{ std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; }
}
GNU GetOpt (LGPL) またはgetoptpp (GPL)などのさまざまな C++ ポートのいずれかを使用できます。
必要なものの GetOpt を使用した簡単な例 ( prog [-ab] input ) は次のとおりです。
// C Libraries:
#include <string>
#include <iostream>
#include <unistd.h>
// Namespaces:
using namespace std;
int main(int argc, char** argv) {
int opt;
string input = "";
bool flagA = false;
bool flagB = false;
// Retrieve the (non-option) argument:
if ( (argc <= 1) || (argv[argc-1] == NULL) || (argv[argc-1][0] == '-') ) { // there is NO input...
cerr << "No argument provided!" << endl;
//return 1;
}
else { // there is an input...
input = argv[argc-1];
}
// Debug:
cout << "input = " << input << endl;
// Shut GetOpt error messages down (return '?'):
opterr = 0;
// Retrieve the options:
while ( (opt = getopt(argc, argv, "ab")) != -1 ) { // for each option...
switch ( opt ) {
case 'a':
flagA = true;
break;
case 'b':
flagB = true;
break;
case '?': // unknown option...
cerr << "Unknown option: '" << char(optopt) << "'!" << endl;
break;
}
}
// Debug:
cout << "flagA = " << flagA << endl;
cout << "flagB = " << flagB << endl;
return 0;
}
GNU GetOpt .
GetOpt を使用した簡単な例:
// C/C++ Libraries:
#include <string>
#include <iostream>
#include <unistd.h>
// Namespaces:
using namespace std;
int main(int argc, char** argv) {
int opt;
bool flagA = false;
bool flagB = false;
// Shut GetOpt error messages down (return '?'):
opterr = 0;
// Retrieve the options:
while ( (opt = getopt(argc, argv, "ab")) != -1 ) { // for each option...
switch ( opt ) {
case 'a':
flagA = true;
break;
case 'b':
flagB = true;
break;
case '?': // unknown option...
cerr << "Unknown option: '" << char(optopt) << "'!" << endl;
break;
}
}
// Debug:
cout << "flagA = " << flagA << endl;
cout << "flagB = " << flagB << endl;
return 0;
}
引数を受け入れるオプションがある場合は、optargを使用することもできます。
さらに別の選択肢として、The Lean Mean C++ Option Parser があります。
http://optionparser.sourceforge.net
これはヘッダーのみのライブラリ (実際には 1 つのヘッダー ファイルのみ) であり、他のすべての提案とは異なり、独立型でもあります。つまり、依存関係はまったくありません。特に、STL への依存はありません。ライブラリのサポートを必要とする例外やその他のものも使用しません。これは、「外国の」ライブラリを導入することなく、プレーン C または他の言語とリンクできることを意味します。
boost::program_options と同様に、その API はオプションへの便利な直接アクセスを提供します。つまり、次のようなコードを記述できます。
if (オプション[ヘルプ]) ... ;
と
int verbosity = options[VERBOSE].count();
boost::program_options とは異なり、これは単純に (ユーザー提供の) 列挙型でインデックス付けされた配列を使用しています。これにより、重量のない連想コンテナの利便性が提供されます。
それは十分に文書化されており、企業向けのライセンス (MIT) を持っています。
TLMC++OP には、行の折り返しと列の配置を行うことができる使用法メッセージ用の優れたフォーマッタが含まれています。これは、プログラムをローカライズする場合に役立ちます。これにより、メッセージが長い言語でも出力が適切に表示されることが保証されるためです。また、80 列の使用法を手動でフォーマットする手間も省けます。
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i],"-i")==0) {
filename = argv[i+1];
printf("filename: %s",filename);
} else if (strcmp(argv[i],"-c")==0) {
convergence = atoi(argv[i + 1]);
printf("\nconvergence: %d",convergence);
} else if (strcmp(argv[i],"-a")==0) {
accuracy = atoi(argv[i + 1]);
printf("\naccuracy:%d",accuracy);
} else if (strcmp(argv[i],"-t")==0) {
targetBitRate = atof(argv[i + 1]);
printf("\ntargetBitRate:%f",targetBitRate);
} else if (strcmp(argv[i],"-f")==0) {
frameRate = atoi(argv[i + 1]);
printf("\nframeRate:%d",frameRate);
}
}
TCLAP
本当に素晴らしい軽量設計で使いやすいです:
http://tclap.sourceforge.net/
おそらく、そのために外部ライブラリを使用したいと思うでしょう。選択できるものはたくさんあります。
Boost には非常に機能豊富な (いつものように) ライブラリBoost Program Optionsがあります。
ここ数年の私の個人的なお気に入りはTCLAP です。完全にテンプレート化されているため、ライブラリやリンクはなく、自動化された「--help」生成やその他の利点があります。ドキュメントの最も単純な例を参照してください。
コマンドラインオプションを自分で処理したいだけの場合、最も簡単な方法は次のとおりです。
vector<string> args(argv + 1, argv + argc);
の上部にありますmain()
。これにより、すべてのコマンドライン引数がstd::string
s のベクトルにコピーされます。次に、無限の呼び出し==
の代わりに、文字列を簡単に比較するために使用できます。strcmp()
例えば:
int main(int argc, char **argv) {
vector<string> args(argv + 1, argv + argc);
string infname, outfname;
// Loop over command-line args
// (Actually I usually use an ordinary integer loop variable and compare
// args[i] instead of *i -- don't tell anyone! ;)
for (auto i = args.begin(); i != args.end(); ++i) {
if (*i == "-h" || *i == "--help") {
cout << "Syntax: foomatic -i <infile> -o <outfile>" << endl;
return 0;
} else if (*i == "-i") {
infname = *++i;
} else if (*i == "-o") {
outfname = *++i;
}
}
}
[編集:argv[0]
プログラムの名前である をargs
-- 修正済みにコピーしていることに気付きました。]
Google ライブラリも利用できます。
本当に、コマンドライン解析は「解決」されています。1つだけ選んでください。
C++ の場合、答えは通常 Boost にあります...
Boost::Program Optionsを試してください。コマンドラインと構成ファイルを読み取って解析できます。
これらのツールは、 GetOptを含む GNU C ライブラリにあります。
Qt を使用していて、GetOpt インターフェイスが好きな場合、froglogicはこちらで素敵なインターフェイスを公開しています。
可能であれば、自分のホーンを鳴らして、私が書いたオプション解析ライブラリを見てみることをお勧めします:dropt。
他の多くの機能にはない機能の1つは、以前のオプションをオーバーライドする機能です。たとえば、シェルエイリアスがある場合:
alias bar="foo --flag1 --flag2 --flag3"
使用したいbar
が--flag1
無効にすると、次のことが可能になります。
bar --flag1=0
Google のgflags
私は C の getopt() が好きですが、私は年をとっています。:-)
利用可能な優れたライブラリがいくつかあります。
Boost Program Optionsはかなり重いソリューションです。これは、Boost Program Optionsをプロジェクトに追加するには、Boostをビルドする必要があり、構文がやや混乱するためです(私の意見では)。ただし、コマンドラインオプションで構成ファイルに設定されているオプションを上書きするなど、ほとんどすべてのことを実行できます。
SimpleOptは、かなり包括的ですがシンプルなコマンドラインプロセッサです。これは単一のファイルであり、構造は単純ですが、コマンドラインのオプションへの解析のみを処理するため、すべてのタイプと範囲のチェックを行う必要があります。これはWindowsとUnixの両方に適しており、Windows用のglobのバージョンも付属しています。
getoptはWindowsで利用できます。Unixマシンと同じですが、多くの場合GPLライブラリです。
AnyOptionは、複雑なコマンドライン オプションを簡単に解析するための C++ クラスです。また、オプション値のペア形式で rsourcefile からオプションを解析します。
AnyOption は、従来の POSIX スタイルの文字オプション ( -n ) と新しい GNU スタイルの長いオプション ( --name ) を実装します。または、POSIX スタイルのオプションを無視するように要求することで、より単純な長いオプション バージョン ( -name ) を使用できます。
ライブラリを使用することをお勧めします。古典的で由緒あるgetoptがあり、他にもあると思います。
これには、すでに作成されたライブラリを使用できます
http://www.boost.org/doc/libs/1_44_0/doc/html/program_options.html
CLPP ライブラリを試してください。コマンド ライン パラメーターを解析するためのシンプルで柔軟なライブラリです。ヘッダーのみでクロスプラットフォーム。ISO C++ および Boost C++ ライブラリのみを使用します。私見では、Boost.Program_options よりも簡単です。
ライブラリ: http://sourceforge.net/projects/clp-parser/
2010 年 10 月 26 日 - 新しいリリース 2.0rc。多くのバグが修正され、ソース コード、ドキュメント、サンプル、コメントの完全なリファクタリングが修正されました。
Boost ライブラリを使用できる場合は、boost::program_options をお勧めします。
STL にも通常の C++/C ランタイム ライブラリにも固有のものはありません。
argstream
と非常によく似てboost.program_option
います。変数をオプションなどにバインドできます。ただし、構成ファイルに保存されているオプションは処理しません。
これが linux/unix の場合、使用する標準的なものは gnu getopt です。
CLPP ライブラリを試してください。コマンド ライン パラメーターを解析するためのシンプルで柔軟なライブラリです。ヘッダーのみでクロスプラットフォーム。ISO C++ および Boost C++ ライブラリのみを使用します。私見では、Boost.Program_options よりも簡単です。
ライブラリ: http://sourceforge.net/projects/clp-parser
2010 年 10 月 26 日 - 新しいリリース 2.0rc。多くのバグが修正され、ソース コード、ドキュメント、サンプル、コメントの完全なリファクタリングが修正されました。
私はいくつかのプロジェクトで GetPot を使用しました: http://getpot.sourceforge.net/
主な機能: すべてが単一のヘッダー ファイルにあり、ビルドの手間がかかりません。マシンのどこかに保存し、ファイル保持に「#include」するだけですmain()
最近更新されていませんが、適切に文書化されており、うまく機能します。
これは、特にコマンド ラインを実行する私のお気に入りの方法ですが、効率が問題になる場合だけではありません。やり過ぎと思われるかもしれませんが、このやり過ぎによるデメリットは少ないと思います。
効率的な C/C++ コマンド ライン処理のために gperf を使用する
短所:
利点:
eclipse のような IDE を使用すると、おそらく gperf の実行プロセスを自動化できるため、設定ファイルと switch ステートメントにオプションを追加し、ビルドを押すだけで済みます...
バッチファイルを使用してgperfを実行し、クリーンアップを行い、sedを使用してインクルードガードを追加しました(gperfで生成された.hppファイルに)...
したがって、ソフトウェア内の非常に簡潔でクリーンなコードと、手動で変更する必要のない 1 つの自動生成されたハッシュ テーブル ファイルです。優先事項として効率がなくても、boost::program_optionsが実際にそれを打ち負かすかどうかは疑問です。
私のコメントとrbaleksandarの回答に続いて、Cのプログラムに渡される引数は文字列値です。現在実行中のプログラムの名前 (常に ) で始まるゼロベースの引数インデックスを与える引数 count ( )が提供されます。これにより、ユーザーがプログラムに提供した引数として、その間のすべての引数が残ります。それぞれは、引数ベクトルに含まれる文字列になります(これは、文字列の配列へのポインターであり、関数パラメーターとして記述されます) 。どの引数がどれです。argc
argv[0]
1 - argc
char *argv[]
char **argv
argv[1]
argv[argc-1]
これにより、それらを分離して、コマンド( cmd
)、オプション( opt
)、最後に引数( arg
) として使用できるようになりますcmd
。
ここで、シェル (bash など) の規則がプログラムに渡される引数に適用されることに注意してください。word-splitting、pathname、および変数展開は、コードが引数を取得する前に適用されます。したがって、通常のシェル分割を防ぐために、引数の周りに単一またはより一般的に二重引用ls -al my file.txt
符を付ける必要があるかどうかを検討する必要があります (たとえば、4
ユーザーがコードに引数を指定することになり、 whilels -al "my file.txt"
またはls -al my\ file.txt
は、3
あなたは期待していました。
これらすべてをまとめると、短い構文解析は次のようになります。switch
(ネストされた s の代わりにa を使用するなど、好きなように自由に行うこともできますif
...)
#include <stdio.h>
int main (int argc, char **argv) {
char *cmd = NULL, /* here, since you are using the arguments */
*opt = NULL, /* themselves, you can simply use a pointer */
*arg = NULL; /* or the argument itself without a copy */
/* looping using the acutal argument index & vector */
for (int i = 1; i < argc; i++) {
if (*argv[i] != '-') { /* checking if the 1st char is - */
if (!cmd) /* cmd is currently NULL, and */
cmd = argv[i]; /* no '-' it's going to be cmd */
else /* otherwise, cmd has value, so */
arg = argv[i]; /* the value will be opt */
}
else /* here the value has a leading '-', so */
opt = argv[i]; /* it will be the option */
}
printf ("\n cmd : %s\n opt : %s\n arg : %s\n\n",
cmd, opt, arg);
return 0;
}
使用例/出力
コードを実行すると、引数が分離され、使いやすいように別のポインターが提供されることがわかります。
$ ./bin/parse_cmd ls -la ./cs3000
cmd : ls
opt : -la
arg : ./cs3000
opt
(複数の値をorにコピーする必要があるコマンド文字列を作成するタスクを課された場合arg
、ポインタを単純に使用することはできなくなり、ストレージを作成する必要があることに注意してください。最初はポインタの代わりに配列を使用するか、必要に応じてmalloc
、calloc
および/または などを使用して動的にストレージを割り当てることができますrealloc
。そうすれば、ストレージ内で値をコピーおよび連結するために使用できるストレージが得られます。)
これがあなたの課題だった場合、ここにあるすべての答えの中で、問題へのアプローチ方法を理解する必要があります。さらに進んで、実際にプログラムに と を実行させる必要がある場合は、実行cmd
する半分離プロセスを生成して、またはに似たもので andopt
を実行することを検討する必要があります。さらに質問がある場合は、コメントを投稿してください。arg
fork
cmd
opt
arg
execv
execvp
CLPPライブラリを試してください。これは、コマンドラインパラメータを解析するためのシンプルで柔軟なライブラリです。ヘッダーのみおよびクロスプラットフォーム。ISOC++およびBoostC++ライブラリのみを使用します。私見では、Boost.Program_optionsよりも簡単です。
ライブラリ:http ://sourceforge.net/projects/clp-parser
2010年10月26日-新しいリリース2.0rc。多くのバグが修正され、ソースコード、ドキュメント、例、コメントの完全なリファクタリングが修正されました。
C/C++ プログラムには常に main 関数があります。次のようになります。
int main(int argc, char**argv) {
...
}
ここで、argc はプログラムに渡されたコマンド ライン引数の数であり、argv はこれらの引数を含む文字列の配列です。そのため、コマンド ラインは、呼び出し元プロセスによって引数に分割されます (これは、Windows のように 1 行ではありません)。
次に、それらを整理する必要があります。
ブーストを使用したくない場合は、この小さなヘルパー クラスをお勧めします。