String zeilenweise abspeichern
-
Hallo zusammen,
ich habe 2 kleine Probleme, an denen ich gerade festhänge.
Und zwar habe ich eine Datei vorliegen, die in etwa so aufgebaut ist:
[Vater] "Name" xyz "Alter" 50 "Beruf" xyz [Mutter] "Name" xyz "Alter" 50 ...Nun ist das in meinen Augen eine ideale Datei um sie in XML abzuspeichern. Also habe ich sie in einen std::string eingelesen. Nun aber zu meinem Problem.
Mit string.find kann ich ja zum Beispiel nach [Vater] suchen lassen (sind mehrere Dateien, die vom Grundgerüst her alle gleich aussehen). Wie kann ich nun eine Zeile tiefer springen um die Merkmale mitsamt Werten auszulesen?
Diese Merkmale (Name, Alter,...) können verschieden heißen, sind aber immer in "" geklammert, genauso wie die Merkmalswerte immer nach einem Leerzeichen folgen. Die Oberbegriffe wie [Vater] [Mutter] usw. stehen fest.
Könnte mir da jemand einen Tip geben? So sehen die paar Zeilen Code aus, die ich bisher habe (Ist nicht viel, aber nach Jahrelangem Javaprogrammieren muss ich mich erstmal wieder an C++ gewöhnen ;))
stringstream strm; // Datei öffnen ifstream in( dateiname ); strm << in.rdbuf(); // Dateiinhalt in den String schreiben string buffer = strm.str();Danke schonmal für die Mühen,
Gruß Lars
-
Hallo Icebar,
ich würde die Datei zeilenweise lesen...
In etwa soifstream in(dateiname); char str[256]; string buffer; while(!in.eof()) { in.getline(str, 255, '\n'); //'\n' ist der delimiter, kann auch weggelassen werden buffer = str; // Sichern der Stringfunktionalität // Weitere Verarbeitung }Zur weiteren Verarbeitung
→ Schauen ob's ein Root element ist (z.B [Vater])
→ Wenn dass der Fall ist weißt du dass jetzt die Kinder kommen.
→ Dann kannst du die Kinder verarbeiten (Suche nach " " und Leerzeichen) bis der nächste Root kommt.
Hoffe das hilft ein wenig...
Gruss
Tobi
-
Hallo Lars,
Jeder dieser Vater- und Mutter-Strukturen bildet - sagen wir mal - einen Block. Abstrakt gesehen willst Du also die Blöcke lesen und in einem XML-Format die Blöcke wieder wegschreiben. Die Grundidee eines Hauptprogramms ist also etwa:
int main() { using namespace std; ifstream von("input.txt"); ofstream nach("output.xml"); copy( istream_iterator< Block >( von ), istream_iterator< Block >(), ostream_iterator< Block >( nach ) ); return 0; }Ein Block besteht aus seinem Namen und einer Liste von 'Eigenschaften' (Properties). Damit es nachher einfach als XMl-Struktur weggeschrieben werden kann, empfiehlt sich erstmal ein kleines Helferlein
struct XmlToken { XmlToken( std::ostream& out, const std::string& name ) : m_out( out << "<" << name << ">" ) , m_name( name ) {} ~XmlToken() { m_out << "</" << m_name << ">"; } private: std::ostream& m_out; std::string m_name; };Dies Ding funktioniert nach dem RAII-Idiom und hüllt alles zwischen Kon- und Destruktor von XmlToken ..
{ XmlToken xt( outStream, "TokenName" ); // weitere Ausgaben nach outStream }in die Struktur <TokenName> ... </TokenName>.
So eine Property hat einen Namen und einen Wert, der ebenfalls nur ein String ist.
struct Property { Property() : m_name(), m_value() {} Property( const std::string& name, const std::string& value ) : m_name( name ), m_value( value ) {} friend std::istream& operator>>( std::istream& in, Property& prop ) { char c; if( in >> c ) { if( c != '\"' ) { in.putback( c ); // wegen weiterlesen in ">> Block" in.setstate( std::ios_base::failbit ); return in; } std::string name, value; if( getline( in, name, '\"' ) >> value ) { prop.m_name.swap( name ); prop.m_value.swap( value ); } } return in; } friend std::ostream& operator<<( std::ostream& out, const Property& prop ) { XmlToken xt( out, prop.m_name ); return out << prop.m_value; } private: std::string m_name; std::string m_value; };und zum Schluß noch der Block
struct Block { Block() : m_name(), m_properties() {} Block( const std::string& name, const std::vector< Property >& props ) : m_name( name ), m_properties( props ) {} friend std::istream& operator>>( std::istream& in, Block& block ) { char c; if( in >> c ) { if( c != '[' ) { in.setstate( std::ios_base::failbit ); return in; } std::string name; if( !getline( in, name, ']' ) ) return in; std::vector< Property > props; std::copy( std::istream_iterator< Property >( in ), std::istream_iterator< Property >(), std::back_inserter( props ) ); in.clear(); // clear failbit, wg. lesen bis Fehler block.m_name.swap( name ); block.m_properties.swap( props ); } return in; } friend std::ostream& operator<<( std::ostream& out, const Block& block ) { XmlToken xt( out, block.m_name ); std::copy( block.m_properties.begin(), block.m_properties.end(), std::ostream_iterator< Property >( out << std::endl, "\n" ) ); return out; } private: std::string m_name; std::vector< Property > m_properties; };vor allem sind noch folgende Includes zu setzen
#include <fstream> #include <iostream> #include <iterator> // i/ostream_iterator; back_inserter #include <string> #include <vector>.. und die Implementierung von main erhält noch eine kleine Erweiterung, damit die XML-Datei ein root-Element bekommt.
int main() { using namespace std; ifstream von("input.txt"); ofstream nach("output.xml"); { XmlToken xt( nach, "root" ); copy( istream_iterator< Block >( von ), istream_iterator< Block >(), ostream_iterator< Block >( nach << "\n", "\n" ) ); } return 0; }Gruß
Werner
-
Hallo,
danke Euch beiden. Die erste Möglichkeit habe ich gestern schon ausprobiert und es hat auch recht gut funktioniert.
Die Tips von Werner werd ich mir heute mal vornehmen
Allerdings habe ich noch ein winziges Problem mit dem Abspeichern von Sonderzeichen ", /, \, usw. in die XML Datei. Aber ich denke auch da werde ich noch eine Lösung finden.Also, herzlichen dank,
Lars