Konsolenparameter auslesen?



  • Hallo,

    ich würde gerne aus der Konsole Paramter auslesen die mit dem programmstart übergeben werden. ALso wenn jemand sein programm so aufruft :

    ./programm "/path/to/file.txt" -1 -par 7 -par2 0.4
    

    soll z.B. deuten das -par 7 den wert 7 in einen paramter schreibt und -par2 in einen anderen. -1 ist einfach ein flag soll also eine boolsche variable setzen.

    Im Moment fällt mir nur ein das ganze in etwas os zu lösen wie unten - doch ich bin mir sicher es geht auch C++-konformer irgndwie über streams vielleicht?

    while (argc-- > 1) 
        {
            switch (Arg(argv[++k])) 
            {
                case 0:     // -par1
                    par1 = std::atof(argv[++k]);
                    argc--;
                    break;
                //weitere case fälle ...
            }
         }
    
    int 
    Class::Arg(char *string)
    {
        int     k = 0;
    
        static char* 
        param_table[] = {
                        "-par1",
                        "-par2"
                        " "   
                        };
    
        do 
            if (!strcmp(string, param_table[k]))
                return k;
        while (*param_table[++k] != ' ');
    
        return -1;
    }
    


  • Vielleicht sowas hier?

    #include <map>
    #include <iostream>
    #include <string>
    
    /*
    template<class Key, class T, class Compare = std::less<Key>,
             class Allocator = std::allocator<std::pair<const Key, T> > >
    class paramap : public std::map<Key, Compare, Allocator>
    */
    class paramap : public std::map<std::string, std::string>
    {
    public:
        int getPar1(int& par1) const;
        int getPar2(float& par2) const;
    
    };
    
    int main(int argc, char* argv[])
    {
        paramap parameters;
    
        for (int i = 1; i < argc; i++)
        {
            if (argv[i][0] == '-')
            {
                if (strlen(argv[i]) > 1 &&
                    i + 1 < argc)
                {
                    if (argv[i+1][0] == '-')
                        continue;
    
                    std::string key(++argv[i]);
                    std::string value(argv[i+1]);
                    parameters.insert(std::pair<std::string, std::string>(key, value));
                }
            }
        }
    
        int par1 = 0;
        float par2 = 0.0f;
    
        if (parameters.getPar1(par1) == 0)
        {
            std::cout << "par1: " << par1 << std::endl;                             
        }
    
        if (parameters.getPar2(par2) == 0)
        {
            std::cout << "par2: " << par2 << std::endl;                             
        }
    
        return 0;
    }
    
    int paramap::getPar1(int& par1) const
    {
        paramap::const_iterator iter = end();
        par1 = 0;
    
        iter = find("par1");
    
        if (iter == end())
            return 1;
    
        par1 = std::atoi(iter->second.c_str());
    
        return 0;
    }
    
    int paramap::getPar2(float& par2) const
    {
        paramap::const_iterator iter = end();
        par2 = 0.0f;
    
        iter = find("par2");
    
        if (iter == end())
            return 1;
    
        par2 = std::atof(iter->second.c_str());
    
        return 0;
    }
    

    Möglicherweise habe ich aber auch deine Frage nicht ganz verstanden, oder aber diese Variante ist ein bisschen zu "C++-konform". Bei Bedarf kann man den Code aber sicherlich zu einem relativ nützlichen Werkzeug ausbauen.



  • Erstmal vielen Dank für Deinen Beitrag und Deine Idee. Eine Frage jetzt: So müsste man ja für jeden Paramter eine eigene Methode schreiben oder? Ich versuche gerade zu sehen ob man es nicht gesammelter in einer Methode schafft...



  • Schau dir mal boost::program_options an 🙂

    EDIT: Übernimmt das Parsen und Bereitstellen der Werte für dich. Alternativ gäbe es noch getopt aus C.



  • Kleine Modifikation:

    main.cpp

    #include <iostream>
    #include <string>
    // Inclusion of .cpp file: for more details see C++s "export" keyword.
    #include "paramap.cpp"
    
    int main(int argc, char* argv[])
    {
        paramap parameters;
    
        for (int i = 1; i < argc; i++)
        {
            if (argv[i][0] == '-')
            {
                if (std::strlen(argv[i]) > 1 &&
                    i + 1 < argc)
                {
                    if (argv[i+1][0] == '-')
                        continue;
    
                    std::string key(++argv[i]);
                    std::string value(argv[i+1]);
                    parameters.insert(std::pair<std::string, std::string>(key, value));
                }
            }
        }
    
        int par1 = 0;
        float par2 = 0.0f;
        bool par3 = false;
    
        if (parameters.getArgTyped("par1", par1) == 0)
        {
            std::cout << "par1: " << par1 << std::endl;                            
        }
    
        if (parameters.getArgTyped("par2", par2) == 0)
        {
            std::cout << "par2: " << par2 << std::endl;                            
        }
    
        // Will always "fail": no bool converter implemented.
        if (parameters.getArgTyped("par3", par3) == 0)
        {
            std::cout << "par3: " << par3 << std::endl;
        }
    
        return 0;
    }
    

    paramap.h

    #ifndef _PARAMAP_H
      #define _PARAMAP_H
    
      #include <map>
      #include <string>
      // Used by converters.
      #include <sstream>
    
      /*
      template<class Key, class T, class Compare = std::less<Key>,
               class Allocator = std::allocator<std::pair<const Key, T> > >
      class paramap : public std::map<Key, Compare, Allocator>
      */
      class paramap : public std::map<std::string, std::string>
      {
      public:
          template<class T>
          int getArgTyped(std::string par, T& arg) const;
    
      protected:
          template<class T>
          int converter(std::string in, T& out) const;
    
      };
    
    #endif
    

    paramap.cpp

    #include "paramap.h"
    
    template<class T>
    int paramap::getArgTyped(std::string par, T& arg) const
    {
        paramap::const_iterator iter = find(par);
    
        if (iter == end())
            return 1;
    
        if (converter<T>(iter->second, arg) != 0)
            return -1;
    
        return 0;
    }
    
    template<class T>
    int paramap::converter(std::string in, T& out) const
    {
        // No appropriate converter implemented...
        return -1;
    }
    
    template<>
    int paramap::converter(std::string in, int& out) const
    {
        out = std::atoi(in.c_str());
        return 0;
    }
    
    template<>
    int paramap::converter(std::string in, float& out) const
    {
        std::istringstream is(in);
        is >> out;
        return 0;
    }
    
    /*
    template<>
    int paramap::converter(std::string in, bool& out) const
    {
        if (in == "1" ||
            in == "true")
        {
            out = true;
            return 0;
        }
    
        if (in == "0" ||
            in == "false")
        {
            out = false;
            return 0;
        }
    
        return 1;
    }
    */
    

    Der gleiche Effekt kann statt der zugegeben etwas seltsamen Verwendung der Spezialisierungen auch mit Überladungen erreicht werden. Jetzt muss nicht mehr für jeden Parameter eine eigene Methode angelegt werden, sondern nur noch für jeden Typ. Obendrein wird jetzt im Vergleich zum vorhergehenden Beispiel eine sauberere Schnittstelle angeboten.



  • Ah sehr interessant kreutzer - herzlichen Dank für die MÜhe. Ich versuche gerade deine Modifikation zu verstehen... 🙂



  • int main(int argc, char * argv[])
    {
    	using namespace std;
    	string somefool;
    	try
    	{
    		boost::program_options::options_description desc("Allowed options");
    		desc.add_options()
    			("somehelp,h", "need help?!")
    			("name,n", boost::program_options::value<std::string>(), "real name")
    
    		boost::program_options::variables_map vmap_;   
    		boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vmap_);
    		boost::program_options::notify(vmap_);
    		if(vmap_.count("somehelp"))
    		{
    			cout << desc << "\n";
    			cout << "YES" << "\n";
    		}
    
    		if(vmap_.count("name")) 
    		{
    			cout << desc << "\n";
    			somefool = vmap_["name].as<string>();
    			cout << somefool << "\n";
    		}
    
    		catch(std::exception & err)
    		{
    			std::cerr << "error: " << err.what() << "\n";
    			return 0;
    		}
    		catch(...)
    		{
    			std::cerr << "unknown exception!" << "\n";
    			return 0;
    		}
    
    		return 0;
    
    }
    

    bb 😉



  • 🙂 ja danke aber ich will nicht unbedingt eine zusätzliche Bib benutzen - klingt blöd - ist aber so...



  • wobei die modifikation von kreuter bei mir nicht kompiliert...



  • mit folgender ausgabe :

    main.cpp:(.text+0x34c): undefined reference to `int paramap::getArgTyped<int>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, int&) const'
    main.cpp:(.text+0x436): undefined reference to `int paramap::getArgTyped<float>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, float&) const'
    main.cpp:(.text+0x520): undefined reference to `int paramap::getArgTyped<bool>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool&) const'
    


  • jo habs doch noch hinbekommen 🙂


Log in to reply