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.