Textfile Editieren



  • Hab jetzt ein wenig überlegt und hab eine Basis (bisher noch ungetestet, sind nur die nieder geschriebene Idee, sonst vergesse ich die bestimmt wieder, bis ich daheim bin...)

    Hier meine Header-Datei zu meiner Klasse:

    #ifndef CTEDIT_H
    #define CTEDIT_H
    
    class CTEdit
    {
    	private:
    		ifstream m_DataIn;
    		ofstream m_DataOut;
    		string m_File;
    
    	public:
    		CTEdit( string szFile );
    		~CTEdit( void );
    
    		void FindLine( string szLine );
    		void ReplaceLine( string szLine, string szNewLine );
    }
    
    #endif
    

    Und hier hier die .cpp-Datei dazu; wie man sieht hänge ich genau an der Stelle, dass ich einen bestimmte Zeile löschen und an dieser Stelle eine neue einfügen muss:

    #include "CTEdit.h"
    
    CTEdit :: CTEdit( string szFile )
    {
    	m_File = szFile;
    }
    
    CTEdit :: ~CTEdit( void )
    {
    
    }
    
    void CTEdit :: FindLine( string szLine )
    {
    	vector<string> results;
    	vector<string>::iterator it;
    	string szLineFromFile;
    	m_DataIn.open( m_File.c_str( ) );
    
    	if( m_DataIn.fail( ) )
    	{
    		cout << "[ERROR] Could Not Open File: " << m_File << endl;
    		return;
    	}
    
    	while( !m_DataIn.eof( ) )
    	{
    		getline( szLineFromFile, m_DataIn );
    
    		if( szLineFromFile[0] == '#' || szLineFromFile.empty( ) )
    			continue;
    
    		if( szLineFromFile.find( szLine ) != string :: npos )
    		{
    			++iResultsCount;
    			result.push_back( szLine );
    			cout << "[CTEDIT] " << (int)results.size( ) << " Lines match '" << szLine << "'" << endl << "[CTEDIT] Content:";
    			for( it = results.begin( ); it < results.end( ); ++it )
    				cout << " " << *it;
    			cout << endl;
    		}
    	}
    }
    
    void CTEdit :: ReplaceLine( string szLine, string szNewLine )
    {
    	vector<string> results;
    	string szLineFromFile;
    	m_DataIn.open( m_File.c_str( ) );
    
    	if( m_DataIn.fail( ) )
    	{
    		cout << "[ERROR] Could Not Open File: " << m_File << endl;
    		return;
    	}
    
    	while( !m_DataIn.eof( ) )
    	{
    		getline( szLineFromFile, m_DataIn );
    
    		if( szLineFromFile[0] == '#' || szLineFromFile.empty( ) )
    			continue;
    
    		if( szLineFromFile.find( szLine ) != string :: npos )
    			result.push_back( szLine );
    
    		if( results.size( ) > 1 )
    		{
    			cout << "[ERROR] Too Many Matches Found For '" << szLine << "'" << endl;
    			return;
    		}
    
    		/*
    		szLine in der Datei löschen
    		szNewLine an der Position von szLine einfügen
    		*/
    
    	}
    }
    


  • Iiiih. Kommst du aus der Steinzeit?



  • Wieso?



  • (Ohne deinen Code gelsesen zu haben) Schau mal hier:
    http://doc.qt.nokia.com/4.7-snapshot/qsettings.html



  • Ich denke ich hab die Antwort jetzt gefunden (tellg() bzw seekg()), allerdings habe ich jetzt ein anderes Problem; mein Programm beendet sich mit einer Warnung "Debug Assertion failed! [...] Expression: String subscript out of range [....]"
    Was bedeutet das? O_o Vermutlich ein Fehler mit meinen Strings... allerdings komme ich nicht drauf was genau...

    void CTEdit :: FindLine( string szLine ) 
    { 
        vector<string> results; 
        vector<string>::iterator it; 
        string szLineFromFile; 
        m_DataIn.open( m_File.c_str( ) );
    
        if( m_DataIn.fail( ) ) 
        { 
            cout << "2[ERROR] Could Not Open File: " << m_File << endl; 
            return; 
        } 
    
        while( !m_DataIn.eof( ) ) 
        { 
            getline( m_DataIn, szLineFromFile ); 
    
            if( szLineFromFile[0] == '#' || szLineFromFile.empty( ) )
                continue;
    
    		szLineFromFile.erase( remove( szLineFromFile.begin( ), szLineFromFile.end( ), '\r' ), szLineFromFile.end( ) );
    		szLineFromFile.erase( remove( szLineFromFile.begin( ), szLineFromFile.end( ), '\n' ), szLineFromFile.end( ) );
    
            if( szLineFromFile.find( szLine ) != string :: npos ) 
                results.push_back( szLine );
    		else
    			continue;
        } 
    
    	cout << "[CTEDIT] " << (int)results.size( ) << " Lines match '" << szLine << "'" << endl << "[CTEDIT] Content: " << results.at( 0 );
        for( it = results.begin( ) + 1; it < results.end( ); ++it )
    		cout << ", " << *it;
        cout << endl;
    	m_DataIn.close( );
    }
    

    Der Fehler tritt nur dann auf, wenn ich in meiner Datei (m_File = "test.txt") eine leere Zeile (also KEIN Inhalt, nicht mal ein Leerzeichen) eingefügt habe (deshalb

    szLineFromFile.erase( remove( szLineFromFile.begin( ), szLineFromFile.end( ), '\r' ), szLineFromFile.end( ) );
    

    )
    Wenn ich die Warnung ignoriere funktioniert es auch problemlos...
    Jemand eine Idee, was das Problem sein könnte?



  • Du solltest mal die Bedingungen in Zeile 19 umtauschen - wenn die eingelesene Zeile leer ist, existiert auch kein szLineFromFile[0] .

    PS: Die beiden erase()-Aufrufe dürften eigentlich keine Auswirkung haben - da getline() sowieso beim Zeilenende aufhört zu lesen.



  • Danke! Das war der Fehler; dachte bisher, dass das zwei oder mehr Voraussetzungen in einer if-Anweisung gleichberechtigt sind O.o

    Wie bereits erwähnt ist die Lösung meines Problems tellg() und seekg() bzw tellp() und seekp().
    Hier mal mein Code:

    void CTEdit :: ReplaceLine( string szOldLine, string szNewLine ) 
    { 
        vector<string> results; 
        string szLine;
    	long pos;
    	m_StreamIn.open( m_File.c_str( ) ); 
    
        if( m_StreamIn.fail( ) ) 
        { 
            cout << "[ERROR] Could Not Open File: " << m_File << endl; 
            return; 
        } 
    
        while( !m_StreamIn.eof( ) ) 
        { 
            getline( m_StreamIn, szLine ); 
    
            if( szLine.empty( ) || szLine[0] == '#' )
                continue;
    
            if( szLine.find( szOldLine ) != string :: npos )
    		{
    			pos = m_StreamIn.tellg( );										// *
    			results.push_back( szLine ); 
    			cout << "Found: " << szOldLine << " at Position " << pos << endl;
    
    		}
    		else
    			continue;
    	}
    
    	if( results.size( ) > 1 ) 
        { 
    		cout << "[ERROR] Too Many Matches Found For '" << szLine << "'" << endl; 
            return; 
        }
    
    	if( results.empty( ) )
    	{
    		cout << "[ERROR] No Matches Found For '" << szLine << "'" << endl; 
            return; 
    	}
    
    	m_StreamIn.close( );
    
    	m_StreamOut.open( m_File.c_str( ), ios :: app );						// Datei für öffnen
    	m_StreamOut.seekp( pos - szLine.size( ) );								// pos : Position festgestellt bei *
    																			// Länge der Zeile wird abgezogen, um wieder an den anfang der Zeile zu kommen
    	m_StreamOut.write( szNewLine.c_str( ), szNewLine.size( ) );				// Schreiben der neuen Zeile
    	m_StreamOut.close( );
    
    }
    

    Leider funktioniert es nicht ganz wie gewünscht, denn anstatt dass eine Zeile ersetzt wird, wird einfach szNewLine am Ende des Inhalts der Datei angehängt... Jemand eine Idee?



  • Hallo,

    vorausgesetzt, dass ich Dich richtig verstehe, dann geht das so nicht.

    Du müsstest:
    1. Das gewünschte File zB. in einen vectorstd::string einlesen.
    2. Alle Manipulationen in diesem Vector vornehmen.
    3. Den gesamten Vector wieder in das File schreiben.

    Ich würde nach dem einlesen das Original File zu *.bak umbenennen.

    Herzliche Grüsse
    Walter



  • Hmpf, ich kann fast nicht glauben, dass das nur so möglich ist?! Gibt es wirklich keine Möglichkeit EINE bestimmte(!) Zeile in einer Datei zu löschen und dann an dieser Stelle etwas neues hineinschreiben? Stattdessen kann man nur die KOMPLETTE File neuschreiben?!



  • Das geht nur, wenn der Ersatztext genauso lang ist wie die Zeile, die du rauslöschen willst. (in dem Fall reicht es, die Daten zu überschreiben)



  • Hallo,

    @CStoll und Z00t
    ich finde den Begriff "Zeile" im zusammenhang mit einem File problematisch. Damit entsteht in unserer Vorstellung fast automatisch das Bild einer "Seite" mit unterschiedlicher Zeilenlänge und jeder Menge Platz um etwas einzufügen.

    @Z00t
    In einem File gibt es keine "Zeilen" oder "Seiten" sondern nur Zeichen (Bytes). So wie das Byte mit dem Wert 65 als "A" interpretiert und dargestellt wird, wird die Bytekombination 10 13 (oder nur 10) als Zeilenende und/oder Zeilensprung interpretiert und dargestellt. Lücken um etwas einzufüllen gibt es in einem File nicht, auch 32 ist keine Lücke.

    Herzliche Grüsse
    Walter



  • Ja, das ist mir durchaus klar 😉
    Deswegen, wenn man sich meinen Code betrachtet, "hole" ich mir ja jede Zeile einzeln, bearbeite diese und speicher gleichzeitig die Position des ifstream nach jeder Zeile. Rein von der Logik her sollte es dann doch möglich sein, wenn ich diese Position habe, mit einem ofstream an eben dieser Position "einzusteigen" und dann weitere Operationen durchzuführen..
    -> http://www.cplusplus.com/reference/iostream/istream/tellg/

    // load a file into memory
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main () {
      int length;
      char * buffer;
    
      ifstream is;
      is.open ("test.txt", ios::binary );
    
      // get length of file:
      is.seekg (0, ios::end);   // get pointer wird ans Ende der File gesetzt
      length = is.tellg();      // tellg() ermittelt die Anzahl der Zeichen von Anfang bis Ende
      is.seekg (0, ios::beg);   // get pointer wird wieder an den Anfang gesetzt
    
      // allocate memory:
      buffer = new char [length];
    
      // read data as a block:
      is.read (buffer,length);
    
      is.close();
    
      cout.write (buffer,length);
    
      delete[] buffer;
      return 0;
    }
    

    Ich mache im Prinzip ja das Gleiche; ich hole mir allerdings nur die Position nach Abschluss einer bestimmten Zeile (ich denke man kann durchaus von "Zeile" besprechen, da ich ja mit getline() arbeite, das ja durchaus eine Zeile "holt") und speicher diese.

    -> http://www.cplusplus.com/reference/iostream/ostream/seekp/

    // position of put pointer
    #include <fstream>
    using namespace std;
    
    int main () {
      long pos;
    
      ofstream outfile;
      outfile.open ("test.txt");
    
      outfile.write ("This is an apple",16); // etwas wird in die datei geschrieben, tellg() ist an der Stelle 16 im ofstream
      pos=outfile.tellp();     // pos wird mit diesem wert versehen
      outfile.seekp (pos-7);   // put pointer wird um 7 Stellen zurückversetzt
      outfile.write (" sam",4); // "n ap" wird überschrieben!
    
      outfile.close();
    
      return 0;
    }
    

    Es ist also durchaus möglich! Allerdings verstehe ich nicht, was ich falsch mache bzw. wieso bei mir nichts überschrieben wird, sondern lediglich am Ende der Datei etwas angehängt wird



  • Ja, du kannst irgendwo in die Datei reinspringen und dort anfangen zu schreiben. Aber damit kannst du nur die vorhandenen Zeichen überschreiben - du kannst weder zusätzliche Zeichen dazwischenquetschen noch Inhalte löschen.
    Deshalb ist auch der übliche Ansatz, die komplette Datei einzulesen, im Speicher zu modifizieren (im RAM Daten rumzuschieben geht wesentlich leichter) und am Ende wieder zurückzuschreiben.



  • Geht das nicht?

    // neue zeile
    
    ofstream out;
    // an einer bestimmten stelle einspringen und schreiben
    out << endl;
    // seekg() um ein zeichen zurück
    


  • Klar geht das - macht nur nicht das, was du vermutlich vorhast. Mit der Anweisung überschreibst du das Zeichen an der Zielposition mit einem Newline.



  • kann man nicht EIN "\n" mit ZWEI "\n\n" ersetzen?



  • Nein, kannst du nicht. Du kannst EIN Zeichen gegen EIN anderes Zeichen ersetzen.

    (bei normalen Speicher-Operationen kannst du auch nur die Werte überschreiben - Einfügen und Löschen funktioniert, indem alle Zeichen hinter der Zielposition verschoben werden, um Platz zu schaffen bzw. die Lücke zu schließen.



  • Ja, das ist mir durchaus klar 😉

    Scheinbar nicht!

    Herzliche Grüsse
    Walter



  • @CStoll
    Alles klar, danke 🙂

    @weicher
    Doch! Durchaus, allerdings war mir nicht klar, was CStoll soeben geschrieben hat 😉


Anmelden zum Antworten