CSS ähnliche Text-Datei parsen



  • Hallo Forum,

    wir haben von der Uni eine CSS ähnliche Datei bekommen, jedoch ist es keine (!) CSS Datei. Es hat eigene Keywords:

    @include "anderePSS.pss";

    /* comment */
    [propertyID="1230000"] {
    fillColor : #f3f1ed;
    minSize : 5;
    lineWidth : 3;
    }

    /* sphere */
    [propertyID="124???|123000"] {
    lineType : dotted;
    }

    /* square */
    [propertyID="125???"] {
    lineType : thinline;
    }

    /* ring */
    [propertyID="133???"] {
    lineType : thickline;
    [hasInnerRing=true] {
    innerLineType : thinline;
    }
    }

    Die Datei oben würde ich nun gerne parsen und ich eine MVC überführen. Ich habe das zwar gelöst mit:

    QRegExp newBracket = QRegExp("\[propertyID=\".*\"\]");
      newBracket.setMinimal(true);
      QRegExp newAttribute = QRegExp("^\\s*(fillColor|minSize|lineWidth|lineType)\\s*:\\s*([\\w@#]+);");
    
      QString currentLine;
    
      while( currentLine = getNextLine() )
      {
    	if (currentLine.contains(newBracket)) 
    	{
            QStringList teileZeile = currentLine.split("=", QString::SkipEmptyParts);
            columnData << "PID";
            columnData << teileZeile.at(1);
            ++number;
        }
          //
          else if (currentLine.contains(newAttribute)) {
            QStringList teileZeile = currentLine.split(":", QString::SkipEmptyParts);
            columnData << teileZeile.at(0);
            columnData << teileZeile.at(1);
            ++number;
        }
      }
    

    Mir wurde gesagt, dass es auf diese Weise nicht sauber wäre (vermutlich weil ich es Zeile für Zeile scanne)? Kann jemand was dazu sagen bzw. kennt jemand eine elegante Lösung?

    Ich entwickel mit dem Qt-Framework, jedoch bin ich nicht darauf beschränkt. Dort kenne ich auch nur DOM Parser für XML Formate.
    Wenn boost beispielsweise eine dafür predestinierte lib hätte, könnte ich diese sehr gerne einbinden.

    Vielen Dank schon mal und VG
    Lespaul





  • lespaul schrieb:

    Mir wurde gesagt, dass es auf diese Weise nicht sauber wäre (vermutlich weil ich es Zeile für Zeile scanne)?

    Nein, weil du mit Regexp nur reguläre Grammatiken erkennen kannst, und das schaut nicht nach einer aus. Zu Lexern und Parsern gibts genug Theorie, deswegen gibts genügend sauberere Methoden, als das mit Regexp hinzupfuschen.
    Ich weiß nicht, ob ich boost::spirit empfehlen würde, hab damit zumindest keine Erfahrung. Ich hätte wahrscheinlich einfach einen recursive descent Parser von Hand geschrieben.



  • lespaul schrieb:

    .. Mir wurde gesagt, dass es auf diese Weise nicht sauber wäre (vermutlich weil ich es Zeile für Zeile scanne)? Kann jemand was dazu sagen bzw. kennt jemand eine elegante Lösung?

    Hallo Lespaul,

    Zeile für Zeile lesen ist selten eine gute Idee. Zumal das Format anscheinend in keiner Weise zeilenorientiert ist.
    Das kann man auch mit C++-Hausmitteln lösen. Etwa so:

    #include <fstream>
    #include <iostream>
    #include <string>
    #include <map>
    #include <vector>
    #include <list>
    
    template< char C >
    std::istream& Char( std::istream& in )
    {
        char c;
        if( in >> c && c != C )
            in.setstate( std::ios_base::failbit );
        return in;
    }
    
    class Entry
    {
    public:
        // einlesen eines Eintrags
        friend std::istream& operator>>( std::istream& in, Entry& prop );
    
    private:
        std::string token_, id_;
        std::map< std::string, std::string > values_;
        std::list< Entry > subProperties_;
    };
    
    // Kommentar und Zeilen ab '@' überlesen
    std::istream& comment( std::istream& in )
    {
        for( char c; in >> c; )
        {
            if( c == '/' )
            {
                in >> Char<'*'>;
                enum class Mode { Idle, StarFound, End };
                for( auto mode = Mode::Idle; mode != Mode::End && in >> c; )
                {
                    switch( mode )
                    {
                    case Mode::Idle:
                        if( c == '*' )
                            mode = Mode::StarFound;
                        break;
    
                    case Mode::StarFound:
                        if( c == '/' )
                            mode = Mode::End;   // Kommentar überlesen
                        else if( c != '*' )
                            mode = Mode::Idle;
                        break;
                    }
                }
            }
            else if( c == '@' )
                in.ignore( 999, '\n' ); // skip line
            else
                return in.putback( c );
        }
        return in;
    }
    
    // einen Eintrag lesen
    std::istream& operator>>( std::istream& in, Entry& prop )
    {
        // Format:                               [         token    =          id    ]             {
        getline( getline( in >> comment >> Char<'['>, prop.token_, '=' ), prop.id_, ']' ) >> Char<'{'>; 
        decltype(Entry::values_) values;
        decltype(Entry::subProperties_) subProperties;
        for( char c; in >> c && c != '}'; )
        {
            if( in.putback(c) && c == '[' )
            {
                Entry p;
                if( in >> p )
                    subProperties.emplace_back( std::move(p) );
            }
            else
            {
                std::string key, value;
                if( getline( in >> key >> Char<':'> >> std::ws, value, ';' ) )
                    values.emplace( std::move(key), std::move(value) );
            }
        }
        if( in )
        {
            prop.values_ = std::move( values );
            prop.subProperties_ = std::move( subProperties );
        }
        return in;
    }
    
    int main()
    {
        using namespace std;
        ifstream file("input.txt");
        if( !file.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -2;
        }
        vector< Entry > entries;
        for( Entry prop; file >> prop; )
            entries.emplace_back( prop );
    
        cout << entries.size() << " Eintraege gelesen" << endl;
        if( !file.eof() )
            cerr << "Fehler beim Lesen" << endl;
    
        return 0;
    }
    

    Gruß
    Werner



  • getline( getline( in >> comment >> Char<'['>, prop.token_, '=' ), prop.id_, ']' ) >> Char<'{'>;
    

    Und du checkst solche Zeilen auch nach paar Wochen noch ? Krass



  • // Format:                               [         token    =          id    ]             {
    

    Dieser Kommentar ist dabei ziemlich hilfreich finde ich.


Anmelden zum Antworten