Parsing Command Line Arguments in C++?

372,926

Solution 1

Boost.Program_options should do the trick

Solution 2

The suggestions for boost::program_options and GNU getopt are good ones.

However, for simple command line options I tend to use std::find

For example, to read the name of a file after a -f command line argument. You can also just detect if a single-word option has been passed in like -h for help.

#include <algorithm>

char* getCmdOption(char ** begin, char ** end, const std::string & option)
{
    char ** itr = std::find(begin, end, option);
    if (itr != end && ++itr != end)
    {
        return *itr;
    }
    return 0;
}

bool cmdOptionExists(char** begin, char** end, const std::string& option)
{
    return std::find(begin, end, option) != end;
}

int main(int argc, char * argv[])
{
    if(cmdOptionExists(argv, argv+argc, "-h"))
    {
        // Do stuff
    }

    char * filename = getCmdOption(argv, argv + argc, "-f");

    if (filename)
    {
        // Do interesting things
        // ...
    }

    return 0;
}

On thing to look out for with this approach you must use std::strings as the value for std::find otherwise the equality check is performed on the pointer values.


I hope it is okay to edit this response instead adding a new one, as this is based on the original answer. I re-wrote the functions slightly and encapsulated them in a class, so here is the code. I thought it might be practical to use it that way as well:

class InputParser{
    public:
        InputParser (int &argc, char **argv){
            for (int i=1; i < argc; ++i)
                this->tokens.push_back(std::string(argv[i]));
        }
        /// @author iain
        const std::string& getCmdOption(const std::string &option) const{
            std::vector<std::string>::const_iterator itr;
            itr =  std::find(this->tokens.begin(), this->tokens.end(), option);
            if (itr != this->tokens.end() && ++itr != this->tokens.end()){
                return *itr;
            }
            static const std::string empty_string("");
            return empty_string;
        }
        /// @author iain
        bool cmdOptionExists(const std::string &option) const{
            return std::find(this->tokens.begin(), this->tokens.end(), option)
                   != this->tokens.end();
        }
    private:
        std::vector <std::string> tokens;
};

int main(int argc, char **argv){
    InputParser input(argc, argv);
    if(input.cmdOptionExists("-h")){
        // Do stuff
    }
    const std::string &filename = input.getCmdOption("-f");
    if (!filename.empty()){
        // Do interesting things ...
    }
    return 0;
}

Solution 3

I can suggest Templatized C++ Command Line Parser Library (some forks on GitHub are available), the API is very straightforward and (cited from the site):

the library is implemented entirely in header files making it easy to use and distribute with other software. It is licensed under the MIT License for worry free distribution.

This is an example from the manual, colored here for simplicity:

#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; }
}

Solution 4

Boost.Program_options

Solution 5

You can use GNU GetOpt (LGPL) or one of the various C++ ports, such as getoptpp (GPL).

A simple example using GetOpt of what you want (prog [-ab] input) is the following:

// 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;
}
Share:
372,926

Related videos on Youtube

Verhogen
Author by

Verhogen

Updated on February 11, 2021

Comments

  • Verhogen
    Verhogen over 3 years

    What is the best way of parsing command-line arguments in C++ if the program is specified to be run like this:

    prog [-abc] [input [output]]
    

    Is there some way of doing this built into the standard library, or do I need to write my own code?


    Related:

    • Mitch Wheat
      Mitch Wheat almost 14 years
      I think the Nunit source code (C#) has a good example of a command line handling class....
    • Andrejs Cainikovs
      Andrejs Cainikovs almost 8 years
      The easiest would be to use one of the argument parsing libraries: getopt or argparse.
    • stefaanv
      stefaanv almost 8 years
      If you can't use libraries (e.g. boost), at least use std::vector<std::string> args(argv, argv+argc); so you can parse a vector of strings instead of an array of char-arrays.
    • Dharma
      Dharma over 6 years
      For people using OpenCV already in their program, cv::CommandLineParser may also be a good choice. [ I mean just in case you are using it already for other purpose, I don't mean to include OpenCV for command line parser.]
    • juzzlin
      juzzlin over 4 years
      Recently wrote this for modern c++: github.com/juzzlin/Argengine
    • droptop
      droptop over 3 years
  • robottobor
    robottobor over 15 years
    or getopt_long if you've got it
  • Aaron
    Aaron over 15 years
    I'm not sure if getopt is available on windows. I know it is in C on GNU
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    my co-worker used it on windows. im quite sure it's available on windows
  • brofield
    brofield about 15 years
    This doesn't help to do anything that can't be already done with argc/argv.
  • Ankit Roy
    Ankit Roy about 15 years
    @brofield: Sure, it doesn't change the world. But I find the == and value semantics simplify things enough that I keep using it.
  • hookenz
    hookenz almost 15 years
    Good choice. Alternatively, if you can't use boost for some reason then the standard c based "getopt" function will also get the job done.
  • Jonathan Graehl
    Jonathan Graehl almost 15 years
    Does it allow you to show the current and default values? A few years ago I had to implement a wrapper around it to show the effective options (based on parsed configs/args, and not just showing the strings supplied).
  • Jonathan Graehl
    Jonathan Graehl almost 15 years
    Looks nice for simple use. I've always liked "options as global variables defined anywhere you please" for small tools. Primitives + string only which can be a big minus.
  • sorin
    sorin about 14 years
    FYI, GNU getopt is GPL and getoptpp is also GPL so boost variant could be better for non open-source software.
  • Hamish Grubijan
    Hamish Grubijan almost 14 years
    @j_random_hacker - what if I want to remember the order of the arguments? For instance, if the user typed mycommand.exe -h file.csv, I want to tell them that they are not using the utility correctly and why (should not supply a file name if they are just using the version). This example is rather simple, but I can think of more convoluted flags. The end result would be: sometimes the order does matter, and sometimes it does not. So ... how should I proceed then? Let me know if you have a question about my question.
  • Ankit Roy
    Ankit Roy almost 14 years
    @Hamish: I'm a bit confused -- loading the strings into a vector doesn't "lose" their order. You can still access the ith argument with args[i] (in fact I often do this myself, as the comment in my code snippet says). The iterator style is just a bit more convenient if you only need to deal with one at a time. Does that answer your question?
  • Dirk Eddelbuettel
    Dirk Eddelbuettel almost 14 years
    Not really as the question was about C++ and Getopt is just plain C. There used to be a C++ variant of it but for some reason it was withdrawn.
  • pm100
    pm100 almost 14 years
    it works fine in c++ tho; its what we use in all our c++ code.
  • Dirk Eddelbuettel
    Dirk Eddelbuettel almost 14 years
    Well yes but you can do much better with e.g. TCLAP. I add or remove one line with new option definition and I do not need to edit code in other place --> not so true with old school getopt.
  • Matthieu M.
    Matthieu M. almost 14 years
    +1, didn't know about tclap and it manages to be lightweight and yet feels complete, I'm definitely going to delve deeper.
  • gatopeich
    gatopeich about 13 years
    The documentation for boost::program_options could be more complete. It is specially difficult to find out how to use files to keep the options, a critical feature.
  • gatopeich
    gatopeich about 13 years
    This seems the most obvious option for C++, but its documentation is not complete enough. Try to find there how to store and retrieve options from a file, an essential feature. I dislike how the code using it looks, specifically the wording options.add_options()(option1)(option2)... which I consider an abuse of C++ grammar.
  • Otto Allmendinger
    Otto Allmendinger over 12 years
    -1: this fetches array elements without bound checks
  • iain
    iain over 12 years
    I Rick you are correct about the #include thanks for updating my answer
  • Poul K. Sørensen
    Poul K. Sørensen over 12 years
    Just a question, do i need to do anything or does this work out of the box? You write to look out for : std::strings - so should i convert char* to a std::string anywhere or ?
  • iain
    iain over 12 years
    This works out of the box. However note that the option parameter is const std::string&. It is important that the value parameter to std::find is a std::string so that std::string::operator==() is used not the char * operator==() (as the latter will only compare the pointer value and not the string contents).
  • Sebastian Mach
    Sebastian Mach about 12 years
    -1: because of no bounds checking
  • Richard
    Richard about 12 years
    Compiling code with Boost.Program_options did not seem straight-forward and required linking options, et cetera, beyond the inclusion of the header file.
  • Richard
    Richard about 12 years
    This option turned out to be the simplest for me, though it did add a sub-directory to my program with a number of header files. The include paths needed editing accordingly.
  • Matthew Flaschen
    Matthew Flaschen about 12 years
    @SorinSbarnea, TINLA, but I believe the license is actually LGPLv2.
  • sorin
    sorin about 12 years
    Sorry but Google Code license on the project page states clearly GPL.
  • Matthew Flaschen
    Matthew Flaschen about 12 years
    @SorinSbarnea, did you look at my link? I should have made it clearer, but I was referring to getopt and getopt-gnu, not getoptpp.
  • Keith Thompson
    Keith Thompson about 12 years
    @RobertMunafo: The references to argv[i+1] can easily go outside the bounds of the argv array. Think about running the program with "-i" as the last argument.
  • Marc Mutz - mmutz
    Marc Mutz - mmutz about 12 years
    @RobertMunafo: correct, argv[argc] is required to be 0. Assigning that to filename and passing it to printf() will crash at runtime, though. And even if we don't fall off the end, the code doesn't sufficiently advance i after it parses an argument. It will try to parse the argument value as an option name next time around the loop.
  • Robert Munafo
    Robert Munafo about 12 years
    -1 for being a "roll-your-own" answer, whereas the question asks specifically for a "library in STL" that will do the task. Also, as noted by Keith Thompson and mmutz, there are some bugs in bounds checking.
  • Sean
    Sean over 11 years
    I've used getopt, google's gflags, program_options from Boost and tclap is fantastic. I can't say enough good things about tclap, especially considering the available alternatives. The extent of the gripes that I have is that it's help formatting is "different" than what my eye is used to.
  • Luis Machuca
    Luis Machuca over 11 years
    You can get pretty much the same for much less. If you want things like --long-option, it's fairly straightforward to do yourself.
  • Sean
    Sean about 11 years
    Starting with version 0.1.3, the license is now MIT. I'm trying this out on a new project instead of TCLAP and so far it looks very promising. The file config option is quite nice.
  • Andrew Larsson
    Andrew Larsson over 10 years
    I just tried out exOptionParser, but it has so many problems. First of all, I get 58 warnings about unsigned int to int conversion. It also tries to increment list iterators (which can't be used like that) and crashes. Its interface is so terrible as well. It uses references all over the place instead of just returning the data you want. It looks like a C library even though it's built on top of the C++ STL.
  • Asherah
    Asherah over 10 years
    This looks pretty neat. Glad I scrolled down; there's just nothing very good for plain C, save this!
  • Moshe Rubin
    Moshe Rubin over 10 years
    I've used all kinds of solutions over the years, including my own home-rolled one. I join the others in extolling the virtues of TCLAP. it was easy to integrate and answers my needs.
  • Stephen
    Stephen over 10 years
    introducing boost to a code base just to parse command line options is a bit "sledgehammer to crack a nut". If boost is there already use it. Otherwise have a look at something like gopt. Nothing against boost in general but its kinda heavyweight and i find that the versions are tied tightly to g++ versions.
  • Joe
    Joe about 10 years
    Looks like the project moved to sourceforge.
  • user18490
    user18490 about 10 years
    I found the comments really harsh. I think it's fair to show also an example of how this can be done without using a library. This answer is complementary +1, sorry.
  • lmat - Reinstate Monica
    lmat - Reinstate Monica almost 10 years
    This doesn't work as expected, for instance, like the tar application: tar -xf file, right? Each option must be separated. grep -ln pattern file wouldn't be understood, but would have to be grep -l -n pattern file.
  • iain
    iain almost 10 years
    Absolutely if you want posix style command line options then you should use one of the command line processing libraries mentioned in other answers. As this answer says this is for simple command line options.
  • Thomas Eding
    Thomas Eding over 9 years
    Getopt is stupidly low level for a C++ program. I highly recommend AGAINST using it for a C++ program.
  • Thomas Eding
    Thomas Eding over 9 years
    Ughh. I understand the use of this library in C code, but IMO, this is way too low level to be acceptable in any C++ application I've ever written. Find a better library if you don't need pure C.
  • Samaursa
    Samaursa over 9 years
    Link for future reference: sourceforge.net/projects/optionparser
  • SmallChess
    SmallChess over 9 years
    Furthermore, boost::program_options is not a header only library. You'll have to build boost. This is very troublesome.
  • naufraghi
    naufraghi almost 9 years
    @JohnShedletsky are you sure? I don't use the lib any more but in the manual are showed both long and short format arguments.
  • johnwbyrd
    johnwbyrd almost 9 years
    TCLAP works, though its Windows roots are showing. It does compile clean on other platforms, but there are lots of Windowsy default choices: flags are separated from parameters by spaces, not equals signs; flags can require only a short (one-letter) and a long representation; multiple arguments per flag handling is wonky. On the plus side, it works on Mac too, it's not infected with GPL, it's quick to understand, it provides a convenient --help command, and it has no real external dependencies.
  • John Shedletsky
    John Shedletsky almost 9 years
    @naugraghi Actually it does work but the error I was getting was misleading.
  • Max Lybbert
    Max Lybbert over 8 years
    (Thank you @QPaysTaxes for noticing the link was broken; I don't know why your edit was rejected, but you were definitely correct).
  • AlwaysLearning
    AlwaysLearning over 8 years
    Why does the distribution have a configuration script and a Makefile if the library is header-only?
  • naufraghi
    naufraghi over 8 years
    @AlwaysLearning Opening the Makefile it seems to be there to run the tests, compile the docs and build the examples.
  • Tomáš Dvořák
    Tomáš Dvořák about 8 years
    This is nice, but two minor improvements were needed: First, constructor params should be const-qualified, second, return value of getCmdOption should be a value, not a reference, otherwise you run into stackoverflow.com/questions/1339601/…. Other than that, a nice and simple solution, I'll use that, thanks.
  • AamodG
    AamodG about 8 years
    An important note: This class does not compile as is with Visual Studio compiler (I'm using 2010 version). You must explicitly include <string> header to your program (iostream and vector are not sufficient). Got this solution from : stackoverflow.com/questions/9080028/…
  • MarcusJ
    MarcusJ almost 8 years
    Sounds great, but it seems a bit too big. Also, I'm just looking for a way to parse out scanf format specifiers from an argument, I've already written my own (albeit more basic) parser.
  • jamesdlin
    jamesdlin almost 8 years
    @MarcusJ It seems a little weird that you say that this is too big (it's much smaller than most other command-line option parser) yet that you want it to parse printf/scanf format specifiers (which isn't something that command-line option parsers typically do)...
  • MarcusJ
    MarcusJ almost 8 years
    Yeah, I know I've got some specific requirements for this, I'm just gonna go ahead and rewrite my option parser, but I did look at your code and the idea of passing in a struct to contain the various options is really interesting (so far mine is just hard coded). It's not too big on it's own, it's just that I've got a single .c/.h project, and your code would double the amount of code I've already got, so it's too big for my specific project.
  • Kemin Zhou
    Kemin Zhou over 7 years
    On one extreme, for really simple programs or you just directly work with argv[] array. Another situation, for total flexibility in your arguments you can work with argv array directly (you can do prog -1 firstinput -2 second input -obj {constructor arguments ..}). Otherwise use boost, tclap, or many others.
  • lrineau
    lrineau over 7 years
    1. @iain What is the license of that piece of code? 2. @TomášDvořák is right about the return reference to a temporary, when getCmdOption returns the empty string. InputParser should have a std::string empty_string as a member, and return its reference when the option is not found.
  • iain
    iain over 7 years
    The first piece of code is mine the second was a edit by someone else. Regarding my code there is no licence it is public domain, feel free to use as you like without any warranty. I am not sure about the second. Regarding the InputParser class I am happy that it was contributed to my answer, but I would not put the args into a vector, but store argv and argv + argc as the begin and end iterators.
  • Admin
    Admin over 7 years
    boost seems like a total overkill for this task
  • Mike Weir
    Mike Weir about 7 years
    Didn't vote this one down, but I'm personally not a fan of something small like this having the dependency of exceptions.
  • Mike Weir
    Mike Weir about 7 years
    Similar to "The Lean Mean C++ Option Parser", but has a much better API. Highly recommended.
  • edW
    edW about 7 years
    There is also a GNU example of getopt with a value e.g. myexe -c myvalue You can try to search for "Example of Parsing Arguments with getopt"
  • Asalle
    Asalle about 7 years
    It does not support any other OS but Windows
  • Asalle
    Asalle about 7 years
    It returns false from functions, that is supposed to return char*, how is that possible? (the function is inline const char* GetPot::__match_starting_string(const char* StartString))
  • Asalle
    Asalle about 7 years
    Incredibly error-prone, in my opinion, e. g. I received a segfault after segfault (in the lib code) while using it
  • Jay
    Jay about 7 years
    @Asalle What makes you say it's windows only?
  • kebs
    kebs about 7 years
    @Asalle No idea, but this seems like an "inside" function, not part of the API. In case you have a doubt, better ask the author.
  • Beginner
    Beginner almost 7 years
    @Robert Munafo the asks for a way to parse the command line and additionally wether there is an STL-lib.
  • 2-complex
    2-complex over 6 years
    It uses strcpy_s. I had some luck getting it to work on macos by writing my own quick-and-dirty strcpy_s that calls strcpy.
  • Peter Mortensen
    Peter Mortensen almost 6 years
    What do you mean by "... is not too immediate to use"? Can you elaborate?
  • Vsevolod A.
    Vsevolod A. almost 6 years
    @iain I am getting an error: no instance of function template "std::find" matches the argument list argument types are: (__gnu_cxx::__normal_iterator<const std::__cxx11::string *, std::vector<std::__cxx11::string, std::allocator<std::__cxx11::string>>>, __gnu_cxx::__normal_iterator<const std::__cxx11::string *, std::vector<std::__cxx11::string, std::allocator<std::__cxx11::string>>>, const std::__cxx11::string)
  • iain
    iain over 5 years
    I am not sure why. I haven't written any c++ in years. From the error you are getting it looks like you are using the code at the bottom added by someone else. Sorry I cannot be more help, but I do not even have a c++ compiler installed that I could test this out with.
  • kraxor
    kraxor over 5 years
    This answer is crying out for a simple example IMHO.
  • Totte Karlsson
    Totte Karlsson over 5 years
    Note; detection of unknown arguments don't work. Also, the header gives compile errors if not placed before other headers. I'll look for another parser..
  • Stéphane
    Stéphane almost 5 years
    Was initially reluctant, but noticed it was in the Ubuntu repo which made it extremely easy: sudo apt-get install libtclap-dev. Within a short amount of time had replaced my custom argv parsing with calls into TCLAP, including a few custom TCLAP::Constraint classes for validation. Works as advertised, will definitely use it again. Thanks for the link, would never otherwise have heard about it.
  • András Aszódi
    András Aszódi over 4 years
    boost::program_options is hopelessly overengineered, difficult to use, and underdocumented. One of the few Boost libraries that would greatly benefit from a complete redesign and rewrite. Don't use it if you can avoid it.
  • MarkWayne
    MarkWayne over 4 years
    I can't think of a less helpful reply to a question. 'It's solved. Pick one.' Sorry, but "duh." With my about um 15 minutes of thinking about this problem, I have come up with about 30 different scenarios about how one could approach it. I suspect the 'correct' response is more akin to explaining how a particular set of concerns would lead to a particular set of code implementations. But, hey, thanks for calling.
  • solstice333
    solstice333 about 4 years
  • finnmglas
    finnmglas almost 4 years
    here's an example example provided by the GNU itself ^^
  • mh8020
    mh8020 over 3 years
    Good solution! - Why add a library for something that can be done in a few lines of rather simple code. Especially if your program doesn't use any other libraries, and considering that C++ doesn't have a standard package manager yet. With bounds checking, as suggested above: e.g. if (strcmp(argv[i], "-i") == 0 && argv[i+1]) { filename = argv[++i]; ... }