effiziente Möglichkeit Monatsname überprüfen



  • Hallo,

    wie kann man sehr effizient überprüfen welcher Monat in der char-Variablen drinnensteht bzw. zuvor vom User eingegeben worden ist?

    ParseMonth(char* pszToken, int& nMonth))
    {
    
      if (strcmp(pszToken, "Jan") == 0)
        nMonth = 1; 
      else if(strcmp(pszToken, "Feb") == 0)
        nMonth = 2;
    
    //usw...
    

    Wenn jetzt Dezember in pszToken gespeichert ist, müssten ja einige Abfragen getätigt werden; kann man das z.B. auch mit einem Switch() lösen?

    Gruß
    Jakob



  • Sicher, einfach das erste Zeichen switchen und bei den mehrdeutigen Fällen auch noch das zweite und dritte überprüfen.



  • Wenn du mit effizient gut lesbar meinst, kannst dus so machen:

    void ParseMonth(char* pszToken, int& nMonth)
    {
    	static const char* names[] = { "Jan", "Feb", "Mar" };
    
    	for(int i = 0; i < sizeof(names) / sizeof(names[0]); ++i)
    		if(strcmp(names[i], pszToken) == 0)
    		{
    			nMonth = i+1;
    			return;
    		}
    }
    

    Die "schönere" Variante nimmt natürlich std::string .
    Wenn dir die Variante zu langsam ist, kannst du auch mit std::map<std::string, int> arbeiten, da kannst du mit O(log n) suchen. Bei 12 Einträgen ist der Geschwindigkeitsvorteil aber nicht messbar.



  • Sprichst du von Laufzeiteffizient oder Programmiereffizienz?
    Ich glaub selbst die dümmste und langsamste Implementierung, die ich mir vorstellen kann ist immer noch ne Mio. mal schneller als die Eingabe des Benutzers.
    Ich würde also eine für dich einfache, erweiterbare und wartbare Lösung suchen.



  • brotbernd schrieb:

    Ich würde also eine für dich einfache, erweiterbare und wartbare Lösung suchen.

    Richtig. Wir müssen nämlich gut gerüstet sein, wenn der Dezember abegeschafft wird.



  • volkard schrieb:

    brotbernd schrieb:

    Ich würde also eine für dich einfache, erweiterbare und wartbare Lösung suchen.

    Richtig. Wir müssen nämlich gut gerüstet sein, wenn der Dezember abegeschafft wird.

    Vielleicht schreibt sich Dezember nach der nächsten Rechtschreibreform ja "Detzember"?
    Ein viel naheliegendes Szenario wäre allerdings der Wunsch, auch englische Monatsnamen zu akzeptieren. Oder 100 andere Sprachen. Das wird ein wunderschönes geswitche. Aber manchen macht sowas ja Spaß. 😉



  • volkard schrieb:

    brotbernd schrieb:

    Ich würde also eine für dich einfache, erweiterbare und wartbare Lösung suchen.

    Richtig. Wir müssen nämlich gut gerüstet sein, wenn der Dezember abegeschafft wird.

    Ich würde bei "erweiterbar und wartbar" jetzt eher an Sachen denken wie:

    • Man möchte neben den Kurznamen auch auf Langnamen prüfen
    • Die Prüfung soll case-insensitiv sein
    • Das Programm wird übersetzt. Neben englischen Namen sollen auch deutsche, französische und chinesische akzeptiert werden
    • ...

    Unter solchen Aspekten halte ich Implementierungen wie

    int MonthNumer(const char *name)
    {
        switch(name[0])
        {
            case 'J':    // Jan, Jun, Jul
                switch(name[1])
                {
                    case 'a':
                        if(name[2] == 'n')
                            return 1;
                        break;
                    case 'u':
                    //...
                }
           //...
        }
    }
    

    fragwürdig.

    Edit: ah, zu spät -.-



  • Ich benutze gern diese Art von Konstrukt:

    #include <map>
    #include <stdexcept>
    #include <string>
    
    namespace {
      class month_map {
      public:
        typedef std::map<std::string, int> map_t;
    
        month_map() {
          months_["Jan"] = 1;
          months_["Feb"] = 2;
          // ...
        }
    
        map_t::mapped_type operator()(std::string const &m) const {
          map_t::const_iterator i = months_.find(m);
          if(i != months_.end()) {
            return i->second;
          }
    
          throw std::invalid_argument("Unbekannter Monat");
        }
    
      private:
        map_t months_;
      };
    
      month_map const month_parser;
    }
    
    int ParseMonth(std::string const &name) {
      return month_parser(name);
    }
    

    ...jedenfalls, wenn du ParseMonth nicht brauchst, bevor main beginnt. Gelegentlich sieht man so etwas auch mit einer statischen instance()-Funktion der Form

    static month_map const &instance() {
        static month_map me;
        return me;
      }
    

    ...das Problem damit ist aber, dass einige gebräuchliche Compiler (MSVC) diese Art der Initialisierung nicht threadsicher hinkriegen.

    Wenn dir ein vernünftiger Hash einfällt, kann sich da auch std::tr1::unordered_map für anbieten - wobei wir hier von 12 Einträgen sprechen, was in einem herkömmlichen balancierten binären Baum (so ist eine std::map idR implementiert) ziemlich wenige Stringvergleiche bedeutet. Man müsste messen, ob unordered_map sich da lohnt.


Anmelden zum Antworten