String einen substring über mehrere Zeilen



  • Okay Sone, habe deine Tipps befolgt danke, wobei das istream_iterator ohne typ angabe bei mir einen Fehler auswirft und std::ifstream auch. Das mit der if Abfrage ist aber gut danke.



  • Helppls1234 schrieb:

    wobei das istream_iterator ohne typ angabe bei mir einen Fehler auswirft und std::ifstream auch.

    Was meinst du, ohne Typangabe? Hast du vergessen istreambuf_iterator mit <char> zu qualifizieren, dann könnte vielleicht so eine Fehlermeldung kommen...?
    Und natürlich alle Header einbinden:

    #include <fstream> // ifstream
    #include <iterator> // istream(buf)_iterator
    

    Also was ist jetzt die genaue Fehlermeldung?



  • Oh, schon gut. Habe mich verlesen ich dachte du meintest ich soll istream_iterator ohne typ dahinter benutzen. Habe das buf überlesen. 😃



  • Hoffentlich liefert SeppJ den INI-Parser trotzdem noch, obwohl das Problem jetzt gelöst ist. 🕶



  • cooldown schrieb:

    Hoffentlich liefert SeppJ den INI-Parser trotzdem noch, obwohl das Problem jetzt gelöst ist. 🕶

    Der von Werner ist gut. Edit: Lol, direkt darüber ist auch einer von SeppJ.

    int toInt = atoi( THEVALUE.c_str() );
    

    Geht vielleicht std::stoi?


  • Mod

    So, irgendwie bin ich ein bisschen verrückt geworden, nachdem die erste Fehlermeldung implementiert war. Da habe ich mir dann Mühe gegeben, wirklich alle Eventualitäten zu berücksichtigen und zu jeder eine aussagekräftige Meldung zu geben. Daher ist der Code doch etwas kompliziert, da viele verschachtelte if-Abfragen. Ich habe mir jedoch Mühe gegeben, möglichst alles mit (relativ) anfängerfreundlichen Mitteln zu machen (map und string sollte man als etwas fortgeschrittener Anfänger kennen und man sollte auch schon mal gehört haben, dass es eine Standardbibliothek gibt und wie man deren Funktionen nachschlägt). Wenn man also dem Programmverlauf folgen kann, dann ist jeder einzelne Ausdruck hoffentlich verständlich. Ok, Templates sind drin, aber keine komplizierten. An der Stelle mag ich einfach keinen Code schreiben, der nur mit int arbeitet.

    #include <iostream>
    #include <string>
    #include <map>
    #include <stdexcept>
    #include <algorithm>
    #include <functional>
    #include <sstream>
    
    namespace utility
    {
      // Copied from Stackoverflow:
      // trim from start
      static inline std::string &ltrim(std::string &s) 
      {
        s.erase(s.begin(), std::find_if(s.begin(), s.end(),
                std::not1(std::ptr_fun<int, int>(std::isspace))));
        return s;
      }
    
      // trim from end
      static inline std::string &rtrim(std::string &s) {
        s.erase(std::find_if(s.rbegin(), s.rend(), 
                std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
        return s;
      }
    
      // trim from both ends
      static inline std::string &trim(std::string &s) {
        return ltrim(rtrim(s));
      }
    };
    
    class SimpleIniParser
    {
      typedef std::map<std::string, std::string> Category;
      typedef std::map<std::string, Category> IniFile;
    
      IniFile data;
    
    public:
    
      SimpleIniParser(std::istream& in)
      {
        std::string line; 
        std::string current_category;
        bool category_still_open = false;
    
        while(getline(in, line))
          {
            utility::trim(line);
            if (line.size())
              {
                if (line[0] != '#')
                  {
                    if (line[0] == '[')
                      {
                        if (line.size() == 1)
                          throw std::logic_error("Error parsing ini-file: "
                                                 "Solitary '[' in file");
                        std::size_t found = line.find(']');
                        if (found == std::string::npos)
                          throw std::logic_error("Error parsing ini-file: "
                                                 "Opening '[' without a closing ']'");
    
                        if (line[1] != '/') // new category
                          {
                            if (category_still_open)
                              throw std::logic_error("Error parsing ini-file: "
                                                     "Category within category not supported.");
                            current_category = line.substr(1, found-1);
                            category_still_open = true;
                          }
                        else  // close old category
                          {
                            std::string closed_category = line.substr(2, found-2);
                            if (!category_still_open or closed_category != current_category)
                              throw std::logic_error("Error parsing ini-file: "
                                                     "Closing a category that is "
                                                     "not opened: " + closed_category);
                            category_still_open = false;
                            current_category = "";
                          }
                      }
                    else  // new entry for category
                      {
                        std::size_t found = line.find('=');
                        if (found == std::string::npos)
                          throw std::logic_error("Error parsing ini-file: Entry without a '='");
    
                        std::string name = line.substr(0, found);
                        utility::rtrim(name);
                        std::string entry = line.substr(found + 1, std::string::npos);
                        utility::ltrim(entry);
    
                        data[current_category][name] = entry;                   
                      }
                  }
              }
          }
        if (category_still_open)
          throw std::logic_error("Error parsing ini-file: Category \"" 
                                 + current_category + "\" never closed.");
      }
    
      template<typename T> void get(std::string const& category, std::string const& name, T &entry) const
      {
        IniFile::const_iterator found_category = data.find(category);
        if (found_category == data.end())
          throw std::logic_error("No category \"" + category + "\" in ini-file.");
    
        Category const& cat_data = found_category->second;
        Category::const_iterator found_entry = cat_data.find(name);
        if (found_entry == cat_data.end())
          throw std::logic_error("No entry \"" + name + "\" in category \"" + category + "\".");
    
        std::stringstream parser(found_entry->second);
        if (!(parser >> entry))
          throw std::logic_error("Could not convert \"" + found_entry->second + 
                                 "\" for entry \"" + name + "\" in category \"" +
                                 category + "\" to the desired type.");
      }
    
      template<typename T> void get(std::string const& category, std::string const& name, 
                                    T &entry, const T& default_value) const
      {
        try
          {
            get(category, name, entry);
          }
        catch (std::logic_error)
          {
            entry = default_value;
          }
      }
    
      template<typename T> void get_verbose(std::ostream &out, std::string const& category, 
                                            std::string const& name, T &entry) const
      {
        get(category, name, entry);
        out << category << "::" << name << "\t=\t" << entry << '\n';
      }
    
      template<typename T> void get_verbose(std::ostream &out, std::string const& category, 
                                            std::string const& name, T &entry, const T& default_value) const
      {
        get(category, name, entry, default_value);
        out << category << "::" << name << "\t=\t" << entry << '\n';    
      }
    };
    

    Anwendungsbeispiel:

    int main()
    {
      SimpleIniParser sip(std::cin);
    
      int a;
      sip.get_verbose(std::cout, "Krieger", "Str", a);
    }
    

    Features und Sonstiges:
    -Kommt mit allerlei Schweinereien bei der Eingabe klar oder bricht zumindest mit einem passenden Fehler ab
    -Unterstützt "freie" Einträge ohne Kategorie. Dies kann auf Wunsch entfernt werden.
    -Unterstützt bis zu einem gewissen Grad auch Namen und Einträge mit Whitespace
    -Momentanes Verhalten beim Parsen des Eintrags entspricht dem operator>>. Das heißt, was rechts vom '=' steht wird bei std::string als Typ an Whitespaces getrennt. Ich bin unentschieden, ob dies das semantisch richtige Verhalten ist. Am besten wäre wohl eine Unterstützung für Quotes einzubauen. Dies zieht aber ganz schön viel Arbeit mit sich, da man dann auch Escapesequenzen als Features möchte und wohl auch mehrzeilige Ausdrücke.
    -Nur flache Hierarchien werden unterstützt, keine Verschachtelung. Dies wäre auf Wunsch leicht einbaubar, aber momentan sehe ich es als Fehler in der Datei an, da das Format einen flachen Aufbau verlangt.



  • Bitte SeppJ ein bisschen kritisieren! Es kann ja nicht sein das er nur austeilt aber nie selbst niedergemacht wird. 😃



  • while(getline(in, line))
    

    Nein! Nein Nein Nein Nein Nein Nein Nein! Nein! 😡 So mag ich das nicht!

    Ich hau auch was raus.


  • Mod

    kleine bitte schrieb:

    Bitte SeppJ ein bisschen kritisieren! Es kann ja nicht sein das er nur austeilt aber nie selbst niedergemacht wird. 😃

    Ja, an dem Code könnte man noch vieles besser machen. Aber es ist/war Sonntagmorgen, da habe ich an einigen Stellen etwas geschlunzt. Ich hoffe, die meisten davon sind mir bewusst 😃 .



  • Also. Ich habe mal eine erste, unvollständige Zwischenversion.
    SeppJ, du solltest einen Eimer bereitstehen haben.

    #include <iostream>
    #include <algorithm>
    #include <functional>
    #include <vector>
    #include <map>
    
    // Die ist ganz nützlich.
    template<typename CharT, typename TraitsT>
    inline std::basic_string<CharT, TraitsT>& rtrim(std::basic_string<CharT, TraitsT>& s, std::locale const& loc)
    {
        s.erase( std::find_if(s.rbegin(), s.rend(), [&](CharT ch){ return !std::isspace<CharT>(ch, loc); }).base(), std::end(s) );
        return s;
    }
    
    template<typename Exception = std::logic_error>
    void Assert( bool b, std::string const& what = std::string() )
    {
        if(!b)
            throw Exception(what);
    }
    
    template<typename CharT, typename TraitsT>
    inline std::basic_istream<CharT, TraitsT>& checked_getline( std::basic_istream<CharT, TraitsT>& is, std::basic_string<CharT, TraitsT>& str, CharT delim, CharT NotAllowed )
    {
        str.clear();
    
        while( is.good() )
        {
            typename TraitsT::int_type i = is.get();
    
            if( TraitsT::eq_int_type( i, TraitsT::eof() ) )
            {
                is.setstate( std::ios_base::eofbit );
                break;
            }
    
            typename TraitsT::char_type ch;
            TraitsT::assign( ch, TraitsT::to_char_type(i) );
    
            Assert( !TraitsT::eq( ch, NotAllowed ), "Invalid character in sequence!" );
    
            if( TraitsT::eq( ch, delim ) )
                break;
    
            str.push_back(ch);
        }
    
        return is;
    }
    
    template<typename CharT,
             typename TraitsT = std::char_traits<CharT>,
             typename ValueT = std::basic_string<CharT, TraitsT> >
    struct Node
    {
        typedef CharT char_type;
        typedef TraitsT traits_type;
    
        typedef std::basic_string<CharT, TraitsT> key_type;
        typedef ValueT value_type;
    
        std::basic_string<CharT, TraitsT> const name;
    
        Node( std::basic_string<CharT, TraitsT> const& name = "Root" ):
            name(name) {}
    
        std::map<key_type, value_type> entries;
    
        std::vector<Node*> child_nodes; // Darf *nicht* verändert werden - Inhalt wird gelöscht
    
        ~Node()
        {
            std::for_each( std::begin(child_nodes), std::end(child_nodes), static_cast<void(*)(void*)>(operator delete) );
        }
    };
    
    template<typename CharT,
             typename Traits,
             typename ValueT = std::basic_string<CharT, Traits> >
    std::basic_istream<CharT, Traits>& operator>>( std::basic_istream<CharT, Traits>& is, Node<CharT, Traits, ValueT>& node )
    {
        CharT const separator = is.widen('='),
                    close_brace = is.widen(']'),
                    newline = is.widen('\n');
    
        typedef std::basic_string<CharT, Traits> string_t;
    
        while( (is >> std::ws).good() )
        {
            if( Traits::eq_int_type(is.peek(), Traits::to_int_type(is.widen('#'))) )
            {
                is.ignore( std::numeric_limits<std::streamsize>::max(), newline );
                continue;
            }
    
            if( Traits::eq_int_type(is.peek(), Traits::to_int_type(is.widen('['))) ) /// Entweder eine neue Node wird eingeleitet, oder diese wird geschlossen ( [/...] )
            {
                is.ignore() >> std::ws;
    
                if( Traits::eq_int_type(is.peek(), Traits::to_int_type(is.widen('/'))) )
                {
                    is.ignore() >> std::ws;
    
                    for( auto const& ch : node.name )
                        Assert( Traits::eq_int_type( is.get(), Traits::to_int_type(ch) ), "Invalid closing tag!" );
    
                    is >> std::ws;
    
                    Assert( Traits::eq_int_type( is.get(), Traits::to_int_type(close_brace) ), "Missing ']' character at closing tag!" );
    
                    break; /// Beenden.
                }
                else // Eine neue, verschachtelte Node
                {
                    string_t name;
    
                    Assert( !checked_getline( is, name, close_brace, newline ).eof(), "Missing ']'-character" );
    
                    rtrim(name, is.getloc());
    
                    Assert( !name.empty(), "Empty node title!" );
    
                    node.child_nodes.push_back( new Node<CharT, Traits, ValueT>{name} );
                    is >> *node.child_nodes.back();
                }
            }
            else /// Eine Key-Value zuweisung
            {
                std::pair<string_t, ValueT> pair;
    
                Assert( !checked_getline( is, pair.first, separator, is.widen('\n') ).eof(), "Invalid syntax - expected separator" );
    
                Assert( !rtrim(pair.first, is.getloc()).empty(), "Empty key!" );
    
                is >> pair.second; /// Hier wird der value eingelesen.
    
                node.entries.insert( std::move(pair) );
            }
        }
    
        return is;
    }
    
    #include <sstream>
    
    int main()
    {
        std::istringstream stream{
    R"(
    [Schurke]
    Str=12 # Hier kommt die Stärke- es gibt auch Kommentare, ja
    Dex mit      Whitespaces = 17
    Int=4
    [/Schurke] # Das ist das close-tag
    
    [Krieger]
    Str=18
    Dex=10
    Int=2
    [/Krieger]
    )" };
    
        Node<char, std::char_traits<char>, unsigned> node;
        stream >> node;
    
        for( auto sub_node : node.child_nodes )
        {
            std::cout << "Name: " << sub_node->name << '\n';
            for( auto const& pair : sub_node->entries )
                std::cout << pair.first << " : " << pair.second << '\n';
        }
    }
    

    -Kommt mit allerlei Schweinereien bei der Eingabe klar oder bricht zumindest mit einem passenden Fehler ab

    Ich glaube zumindest, dass es auch meiner tut. Hab noch nicht getestet.

    -Unterstützt "freie" Einträge ohne Kategorie. Dies kann auf Wunsch entfernt werden.

    Geht bei mir sowieso und kann angepasst werden. Ist aber ja rekursiv, daher muss da ein extra Attribut in die Klasse ( is_root o.ä.).

    -Unterstützt bis zu einem gewissen Grad auch Namen und Einträge mit Whitespace

    "Keys" bei mir ja - values müssen entsprechendes Verhalten haben. Das ist der Preis für die Flexibilität die ich haben wollte.

    -Nur flache Hierarchien werden unterstützt, keine Verschachtelung. Dies wäre auf Wunsch leicht einbaubar, aber momentan sehe ich es als Fehler in der Datei an, da das Format einen flachen Aufbau verlangt.

    :p

    Edit: Kleinen formalen Fehler behoben



  • Jetzt müssten wir ein ganz großes Beispiel-File schreiben, und dann will ich wissen wessen schneller ist.


  • Mod

    Sone schrieb:

    Jetzt müssten wir ein ganz großes Beispiel-File schreiben, und dann will ich wissen wessen schneller ist.

    Während deines noch compiliert hat meines schon mehrere Gigabyte geparsed.



  • SeppJ schrieb:

    Sone schrieb:

    Jetzt müssten wir ein ganz großes Beispiel-File schreiben, und dann will ich wissen wessen schneller ist.

    Während deines noch compiliert hat meines schon mehrere Gigabyte geparsed.

    Ko-kompiliert? 😮

    Und als es gelaufen ist? 😞



  • Gut, was ist, wenn bei mir der Wert-Typ ein String ist und nicht unsigned ? Das beschleunigt es eventuell nochmal.
    Außerdem kann checked_getline nochmal stark beschleunigt werden, in dem man nur am Anfang sentry erstellt und dann per Streambuf einliest.


  • Mod

    Ich glaube, Performance ist hier ziemlich irrelevant. Wenn ich auf den typischen Anwendungsfall optimieren würde (wenige Einträge, wenige Kategorien) würde ich wohl sogar vector statt map nehmen, aber ich wollte dieses pädagogische Beispiel(!) weder unnötig verkomplizieren, noch habe ich hier vor, hier irgendwelche Programmierwettbewerbe an solch einem unsinnigen Beispiel auszutragen. Beim Parsen dürften sich unsere Programme unmerkbar wenig tun, weil die Hauptarbeit letztendlich beim Verarbeiten des Eingabestreams liegt, die bei beiden gleich ist. Ich würde jedoch spontan auf meine Implementierung setzen, was die kleinen Unterschiede angeht. Sie ist einfach unkomplizierter, das ist erfahrungsgemäß deutlich schneller.



  • SeppJ schrieb:

    Ich würde jedoch spontan auf meine Implementierung setzen, was die kleinen Unterschiede angeht. Sie ist einfach unkomplizierter, das ist erfahrungsgemäß deutlich schneller.

    Eine Verschachtelungstiefe von 8(!) Braces würde ich nicht unbedingt als unkompliziert bezeichnen.

    Zudem:

    SeppJ schrieb:

    Da habe ich mir dann Mühe gegeben, wirklich alle Eventualitäten zu berücksichtigen und zu jeder eine aussagekräftige Meldung zu geben.

    Deine Implementierung akzeptiert

    []
    global=local
    =
    [/]
    

    Klar, ist leicht gefixt, aber die Tatsache, dass dir so ein Fehler passiert, zeigt, dass dein Ansatz nicht wirklich einfach und robust ist.

    Und schnell? Nicht wirklich. Jede Zeile wird 3 mal kopiert. Ich behaupte mal, die gleiche Implementierung in Python ist schneller, weil dort gesliced wird. Prägnant und schnell? Fehlanzeige in idiomatischem SeppJ-C++-Stil.

    throw std::logic_error("Error parsing ini-file: Entry without a '='");
    

    Exceptions -- lol? Ich zitiere mal die Doku:

    <a href= schrieb:

    std::logic_error ">It reports errors that are a consequence of faulty logic within the program such as violating logical preconditions or class invariants and may be preventable.

    Jetzt erklär mir mal wie fehlerhafte Ini-Files preventable sind. Und falls sie das sind brauchst du keine sprechenden Fehlermeldungen.

    Spätestens bei

    catch (std::logic_error)
    

    läuten die Alarmglocken. Seriously?

    SeppJ schrieb:

    ich wollte dieses pädagogische Beispiel(!) weder unnötig verkomplizieren

    Fazit: Pädagogisch absolut schädlich.

    Es gibt grundsätzlich zwei Ansätze, wie hier vorgegangen werden könnte. Der eine ist auf Geschwindigkeit optimieren. Das bedeutet in letzter Instanz mit einem Streambuf rumzuspielen und die chars direkt in den endgültigen String einzulesen. Der andere Ansatz ist auf Geschwindigkeit zu pfeifen und eine kurze und korrekte Implementierung zu schreiben.

    (Man beachte, dass der Ansatz von Sone dem schlechtesten aus beiden Ansätzen entspricht, performancemässig die Hölle, weil >>ws viel kostet (sentry+ctype holen), widen viel kostet (ctype holen) und das verdammt häufig aufgerufen wird, jemand mit Ahnung hätte das gecached. Und der Code-Blah ist einfach überwältigend.)

    Hier meine Implementierung:

    typedef std::map<std::string, std::map<std::string, std::string> > ini_file;
    
    std::istream& parse_ini(std::istream& in, ini_file& ini, std::string* err = 0)
    {
      const boost::regex ini_line("\\s*([^\\#]*?)\\s*(\\#.*)?");
      const boost::regex start_cat("\[\\s*([^[]+?)\\s*\]");
      const boost::regex end_cat("\[/\\s*([^[]+?)\\s*\]");
      const boost::regex key_val("([^=]+?)\\s*=\\s*(.*?)\\s*");
    
    #define FAIL_PARSE_IF(condition, msg)           \
      if (condition) {                              \
        if (err) *err = msg;                        \
        in.setstate(std::ios::failbit);             \
        return in;                                  \
      }
    
      std::string category;
      for (std::string l; getline(in, l);) {
        boost::smatch what;
        boost::regex_match(l, what, ini_line);
        l = what[1];
    
        if (boost::regex_match(l, what, end_cat)) {
          FAIL_PARSE_IF(what[1] != category, "closing wrong category, should be " + category);
          category.clear();
        } else if (boost::regex_match(l, what, start_cat)) {
          FAIL_PARSE_IF(!category.empty(), "nested categories not supported");
          category = what[1];
        } else if (boost::regex_match(l, what, key_val)) {
          ini[category][what[1]] = what[2];
        } else {
          FAIL_PARSE_IF(!l.empty(), "not a valid line: " + l);
        }
      }
      FAIL_PARSE_IF(!category.empty(), "category still open: " + category);
    
      return in;
    #undef FAIL_PARSE_IF
    }
    

    Kurz und übersichtlich. Wird mit entsprechendem C++11-Support noch besser (s/boost/std/g + Raw-String literals)

    Ich behaupte sogar, performancemässig ist das gleichauf mit SeppJ und Sone, weil ich nicht in Performance-Fallen getreten bin.


  • Mod

    kritticker schrieb:

    Zudem:

    SeppJ schrieb:

    Da habe ich mir dann Mühe gegeben, wirklich alle Eventualitäten zu berücksichtigen und zu jeder eine aussagekräftige Meldung zu geben.

    Deine Implementierung akzeptiert

    []
    global=local
    =
    [/]
    

    Klar, ist leicht gefixt, aber die Tatsache, dass dir so ein Fehler passiert, zeigt, dass dein Ansatz nicht wirklich einfach und robust ist.

    Das ist Absicht. Wieso sollten keine leeren Bezeichner erlaubt sein?

    Und schnell? Nicht wirklich. Jede Zeile wird 3 mal kopiert. Ich behaupte mal, die gleiche Implementierung in Python ist schneller, weil dort gesliced wird.

    Sone will hier Geschwindigkeit machen. Wen interessiert Geschwindigkeit bei einem ini-Parser? Mich nicht.

    throw std::logic_error("Error parsing ini-file: Entry without a '='");
    

    Exceptions -- lol? Ich zitiere mal die Doku:

    <a href= schrieb:

    std::logic_error ">It reports errors that are a consequence of faulty logic within the program such as violating logical preconditions or class invariants and may be preventable.

    Und?

    Spätestens bei

    catch (std::logic_error)
    

    läuten die Alarmglocken. Seriously?

    Ja.

    Der andere Ansatz ist auf Geschwindigkeit zu pfeifen und eine kurze und korrekte Implementierung zu schreiben.

    Dann mach mal kürzer, deine korrekte Implementierung.

    Hier meine Implementierung:

    Oh, super. #include<iniparserlibrary> hätte ich wohl auch gekonnt und wäre noch kürzer. 🙄



  • SeppJ schrieb:

    Das ist Absicht. Wieso sollten keine leeren Bezeichner erlaubt sein?

    Falls es wirklich gewollt ist, sollte die Sektion "[]" von der globalen Sektion verschieden sein. Wüsste aber ehrlich gesagt nicht, weshalb leere Sections und/oder Keys erlaubt sein sollten (values schon, aber das unterstütze ich ja).

    Sone will hier Geschwindigkeit machen. Wen interessiert Geschwindigkeit bei einem ini-Parser? Mich nicht.

    Für diese Einstellung ist mir dein Code viel zu low-level.

    throw std::logic_error("Error parsing ini-file: Entry without a '='");
    

    Exceptions -- lol? Ich zitiere mal die Doku:

    <a href= schrieb:

    std::logic_error ">It reports errors that are a consequence of faulty logic within the program such as violating logical preconditions or class invariants and may be preventable.

    Und?

    Es ist ganz sicher kein Fehler der Programmlogik, wenn das Ini-File fehlerhaft ist.

    Oh, super. #include<iniparserlibrary> hätte ich wohl auch gekonnt und wäre noch kürzer. 🙄

    <iniparserlibrary> wird es wohl nie in absehbarer Zeit in die Standardlibrary schaffen. Boost.Regex hat das schon (ich warte nur noch auf Unterstützung)


  • Mod

    kritticker schrieb:

    SeppJ schrieb:

    Das ist Absicht. Wieso sollten keine leeren Bezeichner erlaubt sein?

    Falls es wirklich gewollt ist, sollte die Sektion "[]" von der globalen Sektion verschieden sein. Wüsste aber ehrlich gesagt nicht, weshalb leere Sections und/oder Keys erlaubt sein sollten (values schon, aber das unterstütze ich ja).

    Doch, das war ganz bewusst Absicht. Wenn dies anders gewünscht wird, dann kann man dies trivial einbauen, da ohnehin bekannt ist, ob man gerade in einer Section oder im globalen Bereich ist. Also ich fand diese Semantik gut. Da das Format nicht genau vorgegeben war, kann der Programmierer tun, was er für richtig hält.

    Sone will hier Geschwindigkeit machen. Wen interessiert Geschwindigkeit bei einem ini-Parser? Mich nicht.

    Für diese Einstellung ist mir dein Code viel zu low-level.

    Es gibt aber keine Methoden auf höherem Level in C++03.

    throw std::logic_error("Error parsing ini-file: Entry without a '='");
    

    Exceptions -- lol? Ich zitiere mal die Doku:

    <a href= schrieb:

    std::logic_error ">It reports errors that are a consequence of faulty logic within the program such as violating logical preconditions or class invariants and may be preventable.

    Und?

    Es ist ganz sicher kein Fehler der Programmlogik, wenn das Ini-File fehlerhaft ist.

    Und?

    Oh, super. #include<iniparserlibrary> hätte ich wohl auch gekonnt und wäre noch kürzer. 🙄

    <iniparserlibrary> wird es wohl nie in absehbarer Zeit in die Standardlibrary schaffen. Boost.Regex hat das schon (ich warte nur noch auf Unterstützung)

    Der Code ist C++03 und es geht darum zu zeigen, wie man so etwas macht. Fertige Bibliotheken gibt es für das Problem haufenweise.



  • @kritticker
    Nette Lösung gegen den Bloat der hier, wie leider üblich, sonst gepostet wurde.


Anmelden zum Antworten