Wie kann ich variable Zahlen aus einer Zeile, die in einem Buffer ist, extrahieren?



  • Hallo an Alle,

    ich bin mom Schüler an einem Tech-Gym. und liebe die PC -Welt. Leider bin ich mit C++ noch nicht so fit und habe erst angefangen damit (4 Monate).

    Es geht um folgendes.

    - Ich habe eine Datei text.txt in der volgendes steht:

    35.121: [Full GC (System) [PSYoungGen: 51172K->0K(764608K)] [PSOldGen: 0K->50789K(1747648K)] 51172K->50789K(2512256K) [PSPermGen: 73515K->73515K(147072K)], 0.3471350 secs] [Times: user=0.33 sys=0.02, real=0.35 secs]

    😕 Wie kann ich nun genau die fetten Zeichen aus der Datei extrahieren und in eine Neue txt mit leerzeichen(Trennzeichen) schreiben?? 😕

    35.121 51172 764608 50789 1747648 51172 50789 2512256 73515 73515 147072

    - Problem es sind ca. 10 Zeilen mit dem gleichen Aufbau, aber mit unterschiedlichen Zahlenwerten, so dass ich nicht konkret suchen kann
    - Das Problem beim Finden eines Algorythmus ist, dass ich auch den Punkt bei er ersten Zahl benötige.
    - Habe schon mit isdigit und cases probiert ran zu kommen, aber es gestaltet sich soooo schwierig, da ich ja nicht nur Zahlen haben will und auch nicht alle Zahlen(habe versucht ab dem Komma zu löschen) 😞 ..ich bräuchte etwas flexibles. 😕

    Folgendes habe ich schon zusammenbekommen um den Inhalt der Datei in eine neue zu schreiben, was auch die Basis für die implementierung eines Algorythmus gewesen ist.

    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    void copyLine4Line( ifstream& istream, ofstream& ostream);
    
    int main(int arg, char **argv)											// Max. Anzahl von Zeichen die in Puffer passen, Zeiger auf ein char-Array
    {
    // -----Datei zum Lesen öffnen--------------------------------------------
    
    	ifstream f1 ("test.txt");											// Legt File-Stream zum lesen und Dateipuffer an, ohne Datei zu öffnen
    	ofstream f2 ("backtest.txt");										// Legt File-Stream zum schreiben und Dateipuffer an, ohne Datei zu öffnen
    
        if (f1.is_open() != true){											// Test
    		cerr << "Konnte test.txt nicht oeffnen" << endl;				// 
    	}																	//	
    
    	if(f2.is_open() != true){											// Test
    		cerr << "Konnte backtest.txt nicht oeffnen" << endl;			//
    	}																	//
    
    	// -Zeilenweise kopieren---------------------------------------------
    	copyLine4Line (f1, f2);												// Aufruf
    	f1.close();															// Schließen
    	f2.close();															//	   "
    
    	return 0;															//
    }																		//
    
    // -----Zeilenweise kopieren in neue Datei--------------------------------
    void copyLine4Line( ifstream& istream, ofstream& ostream){				//
    	char buffer[512];													//
    	while(istream.getline(buffer, sizeof(buffer)) ){					// Einlesen
    		if(istream.fail() ) {											// Test
    			cout << "Unerwarteter Lesefehler" << endl;					//
    			break;														//
    		}																//
    

    Hier habe ich versucht die Zeile zu selektieren z.B.über isdigit, bevor es in der nächsten Zeile in die Datei rausgeschriben wird..aber ging nieeee 😞

    ostream << buffer << endl;										// Rausschreiben
    		if (ostream.fail() ) {											// Test
    			cout << "Unerwarteter Schreibfehler" << endl;				//
    			break;														//
    		}																//
    	}																	//
    }																		//
    //------------------------------------------------------------------------
    

    Da mir auch mein C++ Schinken 😡 von GallileoComputing nicht weiterhilft und ich so etwas spezielles noch in keinem Forum gefunden habe, hoffe ich dass Ihr mir eventuell weiterhelfen könntet.

    Über Antworten würde ich mich sehr freuen!!!!
    Schöne Grüße
    Fill



  • Hallo!

    Willkommen im Forum!
    Bevor wir deine Daten parsen, bietet es sich an, erstmal einen Datentyp "Eintrag" zu erstellen, in dessen Objekten wir die Zahlen dann ablegen. Dazu ist es notwendig, erstens Datentyp, zweitens Bedeutung der Zahlen zu kennen.

    Ist die erste Zahl beispielsweise eine Kommazahl, oder eine Ganzzahl mit Tausenderpunkt?

    Grundsätzlich kannst du mit dem operator >> des std::istream und den Funktionen .get() und .putback() arbeiten, aber wenn du uns sagst, was die Zahlen überhaupt sind, können wir dir bestimmt besser helfen.

    Grüße,
    Der Kellerautomat


  • Mod

    Mit mehr Hintergrundinformationen wäre das natürlich viel leserlicher, da man dann nicht zahl1, zahl2, usw. schreiben würde. Außerdem habe ich natürlich keine Ahnung, worauf es bei dem Format genau ankommt. Ich habe hier angenommen, dass der nicht-fett geschriebene Text im Beispiel immer gleich ist. Um dir eine Idee zu geben, sollte es reichen:

    #include <iostream>
    #include <limits>
    
    class Daten
    {
      double zahl1;
      unsigned zahl2;
      unsigned zahl3;
      unsigned zahl4;
      unsigned zahl5;
      unsigned zahl6;
      unsigned zahl7;
      unsigned zahl8;
      unsigned zahl9;
      unsigned zahl10;
    
      friend std::istream& operator>>(std::istream &in, Daten& daten)
      {
        const std::streamsize max = std::numeric_limits<std::streamsize>::max();
        in >> daten.zahl1;
        in.ignore(1);
        in.ignore(max, ':');
        in >> daten.zahl1;
        in.ignore(max,'(');
        in >> daten.zahl2;
        in.ignore(max,'>');
        in >> daten.zahl3;
        in.ignore(max,'(');
        in >> daten.zahl4;
        in.ignore(max,']');
        in >> daten.zahl5;
        in.ignore(max,'>');
        in >> daten.zahl6;
        in.ignore(max,'(');
        in >> daten.zahl7;
        in.ignore(max,':');
        in >> daten.zahl8;
        in.ignore(max,'>');
        in >> daten.zahl9;
        in.ignore(max,'(');
        in >> daten.zahl10;
        in.ignore(max,'\n');  
        return in;
      }
    
      friend std::ostream& operator<<(std::ostream &out, const Daten& daten)
      {
        return out << daten.zahl1 << ' '
                   << daten.zahl2 << ' '
                   << daten.zahl3 << ' '
                   << daten.zahl4 << ' '
                   << daten.zahl5 << ' '
                   << daten.zahl6 << ' '
                   << daten.zahl7 << ' '
                   << daten.zahl8 << ' '
                   << daten.zahl9 << ' '
                   << daten.zahl10;
      }
    };
    
    #include <sstream>
    int main()
    {
      std::stringstream beispiel("35.121: [Full GC (System) [PSYoungGen: 51172K-> \
    0K(764608K)] [PSOldGen: 0K->50789K(1747648K)] 51172K->50789K(2512256K) [PSPerm\
    Gen: 73515K->73515K(147072K)], 0.3471350 secs] [Times: user=0.33 sys=0.02, rea\
    l=0.35 secs]\n35.121: [Full GC (System) [PSYoungGen: 51172K->0K(764608K)] [PSO\
    ldGen: 0K->50789K(1747648K)] 51172K->50789K(2512256K) [PSPermGen: 73515K->7351\
    5K(147072K)], 0.3471350 secs] [Times: user=0.33 sys=0.02, real=0.35 secs]\n35.\
    121: [Full GC (System) [PSYoungGen: 51172K->0K(764608K)] [PSOldGen: 0K->50789K\
    (1747648K)] 51172K->50789K(2512256K) [PSPermGen: 73515K->73515K(147072K)], 0.3\
    471350 secs] [Times: user=0.33 sys=0.02, real=0.35 secs]\n");
    
      for(Daten daten; beispiel >> daten; std::cout << daten << '\n');
    }
    

    C++ klingt aber eigentlich eher nach einer ungünstigen Wahl für diese Aufgabe, wenn du die Daten einfach nur wieder in eine Datei schreiben möchtest, statt sie in einem C++-Programm weiter zu verarbeiten. Eine Scriptsprache mit RegEx wäre eigentlich eine viel bessere Wahl. Du siehst an meinem Beispiel, dass das in C++ recht frickelig ist.



  • Hallo erstam und herzlichen Dank für die Antwort.

    Sorry das ich diese Zusatzinformationen vergessen habe anzugeben.
    Also:

    - Es handelt sich bei dieser Zeile um eine Speicherauslastung und Bereinigung meines Servers die in eine Datei geschrieben wird

    - Der erste Zahlenwert ist der Zeitstempel, ich gehe davon aus alles vor dem Punkt sind Sekunden, hinter dem Punkt Millisec.

    - Die anderen Zahlen räpresentieren die Speicher(Haupt-Sp./Statischer-Sp.)jeweils "Wieviel gerade belegt ist" dann "die Bereinigungsmenge" und "der Max-Speicher"

    Ich hoffe das Hilft ein wenig um die Zeile nachvolziehen zu können.

    ICh versuche der weil mal einen Datentyp "Eintrag" zu erstellen.



  • Hallo Fill,

    wie Kellerautomat schon sagte, wäre Schritt 1, eine Struktur Eintrag (oder so) zu bauen, die sozusagen der Datensatz ist, der den Inhalt einer Zeile beschreibt. Für diese struct Eintrag baue man sich die sogenannten Streaming-Operatoren und dort findet dann das eigentliche Lesen und Schreiben genau eines Eintrags statt.
    In Deinem Fall ist es sicher angebracht, sich auf markante Zeichen in dem Text zu konzentrieren, die möglichst nur vor der jeweils nächsten Zahl auftauchen. Solche Stellen kann man mit der Methode ignore gezielt 'anspringen'.
    Also bei "35.121**:** [Full GC (System) [PSYoungGen**:** 51172K->0K**(**764608K)] ... " ist das der ':' und die '('. Bei der zweiten Zahl (hier 51172K) muss man schon aufpassen, da der ':' bis dahin zweimal auftaucht.

    Ich habe das ganze hier mal skizziert:

    #include <algorithm> // copy
    #include <fstream>
    #include <iterator> // i(o)stream_iterator
    #include <iostream>
    #include <limits> // numeric_limits
    
    struct Eintrag
    {
        static int const N = 10;
        double prefix_;
        int ps_[N];
    };
    // --   Streaming-Operator für das Lesen
    // 1 "35.121: [Full GC (System) 
    // 2      [PSYoungGen: 51172K->0K(764608K)] 
    // 3      [PSOldGen: 0K->50789K(1747648K)] 
    // 4          51172K->50789K(2512256K) 
    // 5      [PSPermGen: 73515K->73515K(147072K)],
    //      .. Rest bis EOL"
    std::istream& operator>>( std::istream& in, Eintrag& e )
    {
        const std::streamsize ALL = std::numeric_limits< std::streamsize >::max();
        in >> e.prefix_;
        in.ignore( ALL, ':' ); // ersten ':' hinter 'prefix' überspringen
        const char* tags = ":(>(]>(:>(";
        const char* tag = tags;
        for( int i=0; i<Eintrag::N; ++i, ++tag )
            in.ignore( ALL, *tag ) >> e.ps_[i];
        return in.ignore( ALL, '\n' ); // Rest der Zeile überlesen
    }
    // --   Streaming-Operator für das Schreiben
    std::ostream& operator<<( std::ostream& out, const Eintrag& e )
    {
        out << e.prefix_ << " ";
        std::copy( e.ps_, e.ps_ + Eintrag::N, std::ostream_iterator< int >( out, " " ) );
        return out;
    }
    
    int main()
    {
        using namespace std;
        ifstream f1("test.txt");
        ofstream f2("backtest.txt");
        if( !f1.is_open() || !f2.is_open() )
        {
            cerr << "Fehler beim Oeffnen einer der Dateien" << endl;
            return -2;
        }
        copy( istream_iterator< Eintrag >(f1), istream_iterator< Eintrag >(), ostream_iterator< Eintrag >(f2, "\n") );
        return 0;
    }
    

    Gruß
    Werner

    Ups: SeppJ war schneller, aber ich habe alle Zahlen 😉
    @Edit: Schleife beim Lesen



  • Na ja aus den Zahlen wollte ich dann nachher eventuell ein kleines Diagramm zeichnen lassen. 😃

    Aber so weite Schritte kann ich nicht überblicken, daher wollte ich in kleinen Schritten vorrangehen.

    Man hat mir auch schon gesagt das Java besser gewesen wäre, aber ich wollte halt was in c++ machen da ich java nicht kann um da etwas fitter zu werden und dan kam mir halt so eine "hirnrissige idee" mit dieser Datei😋



  • Ihr seid ja mal der Hammer ich doktor da Wochen dran rum und Ihr braucht 5 Minuten 😞
    irgendwie deprimiert das:)

    Auf jeden fall ma herzlichen Dank



  • FillColin schrieb:

    Man hat mir auch schon gesagt das Java besser gewesen wäre, aber ich wollte halt was in c++ machen da ich java nicht kann um da etwas fitter zu werden und dan kam mir halt so eine "hirnrissige idee" mit dieser Datei😋

    Mit Java hast du keine Vorteile - eher mit Python/Perl/Ruby oae.



  • zum Einlesen des Eintrags ist mir noch eine weit lustigere Variante eingefallen, als mein erster Vorschlag oben:

    // --   Streaming-Operator für das Lesen
    // 1 "35.121: [Full GC (System)
    // 2      [PSYoungGen: 51172K->0K(764608K)]
    // 3      [PSOldGen: 0K->50789K(1747648K)]
    // 4          51172K->50789K(2512256K)
    // 5      [PSPermGen: 73515K->73515K(147072K)],
    //      .. Rest bis EOL"
    std::istream& operator>>( std::istream& in, Eintrag& e )
    {
        in >> e.prefix_;
        //                    +--- Zeichen für 'Zahl lesen'
        //                    |+-- lese bis zum folgenden Zeichen
        //                    ||             |3       |4        |5
        const char* format = "%_:[_[_:%_(%_)_->%K(%K)]%K->%K(%K)[_:%K->%K(%K)]_\n";
        int i=0;
        for( const char* p = format+2; *p; ++p )
            *p == *format ? in >> e.ps_[i++]: *p == *(format+1)? in.ignore( std::numeric_limits< std::streamsize >::max(), *++p ): in >> check( *p );
        return in;
    }
    

    Dazu braucht man noch die struct check :

    struct check
    {
        check( char c ) : c_( c ) {}
        friend std::istream& operator>>( std::istream& in, check ch )
        {
            char c;
            if( in >> c && c != ch.c_ )
                in.setstate( std::ios_base::failbit );
            return in;
        }
    private:
        char c_;
    };
    

    Die Variante ist sicherer, was den Syntax-Check angeht, und auch universeller einsetzbar.

    Gruß
    Werner


  • Mod

    jetzt noch mit tmp? ist ja nicht viel anders als brainfuck... :p



  • Na ja mit dem letzten code kann ich mal gar nichts anfangen, da verbiegen sich bei mir die Hirnwindungen 😮 , leider bin ich ja auch noch lange nicht so fit wie ihr 😃
    _____________________________________________

    Aber hätte noch eine Frage??? 😕

    Warum muss ich "FETT" das nochmal aufrufen und ohne File Angabe? Oder ist das nur ein Platzhalter?

    copy( istream_iterator< Eintrag >(f1), istream_iterator< Eintrag >(), ostream_iterator< Eintrag >(f2, "\n") );

    Werner Salomon schrieb:

    Hallo Fill,

    ... copy( istream_iterator< Eintrag >(f1), istream_iterator< Eintrag >(), ostream_iterator< Eintrag >(f2, "\n") );...


  • Mod

    FillColin schrieb:

    Warum muss ich "FETT" das nochmal aufrufen und ohne File Angabe? Oder ist das nur ein Platzhalter?

    copy( istream_iterator< Eintrag >(f1), istream_iterator< Eintrag >(), ostream_iterator< Eintrag >(f2, "\n") );

    Das copy muss wisssen, bis wohin kopiert werden soll. Ein istream_iterator ohne Streamargument im Konstruktor steht für das Ende eines Streams. Siehe:
    http://www.cplusplus.com/reference/std/iterator/istream_iterator/
    http://www.cplusplus.com/reference/algorithm/copy/



  • FillColin schrieb:

    Na ja aus den Zahlen wollte ich dann nachher eventuell ein kleines Diagramm zeichnen lassen. 😃

    Man hat mir auch schon gesagt das Java besser gewesen wäre, aber ich wollte halt was in c++ machen da ich java nicht kann um da etwas fitter zu werden und dan kam mir halt so eine "hirnrissige idee" mit dieser Datei😋

    Du solltest uns etwas mehr über deine Ziele mitteilen.
    Zahlenwerte aus einer Datei rauslesen, geht so gut wie mit jeder Programmiersprache und sogar mit Applikationen.

    Nimm eine Tabellenkalkulation Excel oder OpenOffice:

    • Kann auch Diagramme erzeugen
    • Kann strukturierte Text-Dateien einlesen


  • Nochmal Hallo,

    alles funktioniert nun einwandfrei *freu*
    --- Auf basis von "Werner Salomon" 22:41:49 05.07.2012---

    Nur hat meine Aufzeichnung nun einen "kritischen Wert" erreicht.

    126032.403: [Full GC (System) [PSYoungGen: 2400K->0K(764608K)] [PSOldGen: 58257K->51512K(1747648K)] 60657K->51512K(2512256K) [PSPermGen: 81683K->81341K(82112K)], 0.3413080 secs] [Times: user=0.34 sys=0.00, real=0.34 secs]

    Wenn nun mein Zeitstempel immer größer wird "FETT", dann ist nachher nur noch 126032 zu sehen. Der rest .403 wird verschluckt.

    Nun habe ich versucht im Programm die Datentypen zu vergrößern also "long", dies hatte aber nicht den gewünschten Erfolg.

    Dann habe ich mal etwas mit Zahlen gespielt
    z.b. 200000000 der wird schön ausgegeben, aber sobald was mit Komma kommt schmeist der das weg z.B. 200000000.222

    Kann es sein, dass das Programm automatisch castet und rundet und wenn ja wie unterbinde ich das?

    Scchöne Grüße
    Fill



  • float und double sind mMn. denkbar schlechte Typen für Zeitstempel. Zumindest wenn der Zeitstempel exakt gespeichert/verarbeitet werden soll. Das geht mit float/double nämlich nur, wenn die binäre (!) Darstellung der Kommazahl ausreichend wenig signifikante Stellen hat.

    Was noch dazukommt: je nach verwendeter Funktion (printf, operator << (ostream, ...), ...) werden floats/doubles per Default unterschiedlich genau formatiert. Und das kann eben dazu führen, dass Kommastellen die eigentlich da wären einfach nicht angezeigt werden.

    Kann man natürlich alles mittels diverser Formatierungs-Optionen umstellen, aber es bleibt immer noch das Problem dass sich Dezimal-mit-Kommastellen nicht gut mit Binär-mit-Kommastellen verträgt.



  • Hallo Fill,

    hustbaer hat wahrscheinlich recht mit seinem Verdacht, dass es sich nur um ein Darstellungsproblem handelt. Ein double reicht für 126032.403 locker aus.
    Füge in meinem Listing vor dem 'copy( istream_iterator<>..' noch eine Formatierung für double ein:

    f2.precision(3);
        f2 << fixed;
        copy( istream_iterator< Eintrag >(f1), istream_iterator< Eintrag >(), ostream_iterator< Eintrag >(f2, "\n") );
    

    Damit erzwingst Du, dass double mit 3 Nachkommastellen ausgegeben wird.

    Gruß
    Werner



  • Danke für die thematisch Erörterrung!!

    Sind diese Delauft-Werte in denen automatisch und unterschiedlich Formatiert wird eigentlich willkürlich ausgedachte Standardts oder entspricht das einer durchdachten Norm von c++.

    Habe ich nur das Gefühl, dass das suboptimal ist oder ist meine Aufgabe eher Suboptimal 😃

    Dabei dachte ich das c++ mehr Vorgaben/-schriften bekommen hat wie C um es "sicherer/einfacher" zu machen und nun muss ich quasi doch so spitzfindig sein? 🙄
    ____________________________________________
    Auf jeden fall super lieben Dank für die Komments bis jetzt, ihr habt mich echt weitergebracht und es macht sogar richtig spaß in diesem Forum

    Grüße


  • Mod

    Der Anfangswert für precision ist laut Standard als 6 definiert.



  • FillColin schrieb:

    Dabei dachte ich das c++ mehr Vorgaben/-schriften bekommen hat wie C um es "sicherer/einfacher" zu machen und nun muss ich quasi doch so spitzfindig sein? 🙄

    Nein, du musst bloss den passenden Datentyp verwenden 🙄



  • Hallo, ich bin's wieder.

    zu meiner Freude läuft das Programm auf basis von WERNER SALOMON (Thread 12:43:51 05.07.2012) einwandfrei und konnte es gut weiterverarbeiten.

    Nun habe ich feststellen müssen, das in meine Textdatei 1 mal im Monat ein Sondereintag (Sonderprüfung) stadtfindet. Und zwar kein Full GC sondern nur ein CG

    ....
    3636.211: [Full GC (System) [PSYoungGen: 6520K->0K(764608K)] [PSOldGen: 50789K->56413K(1747648K)] 57309K->56413K(2512256K) [PSPermGen: 82267K->82267K(172288K)], 0.3546920 secs] [Times: user=0.35 sys=0.00, real=0.36 secs]
    7237.275: [GC [PSYoungGen: 91438K->2416K(764608K)] 147852K->58829K(2512256K), 0.0123960 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
    7237.287: [Full GC (System) [PSYoungGen: 2416K->0K(764608K)] [PSOldGen: 56413K->58662K(1747648K)] 58829K->58662K(2512256K) [PSPermGen: 82381K->82381K(170048K)], 0.3349310 secs] [Times: user=0.33 sys=0.00, real=0.34 secs]
    ...

    Ab hier bricht das Programm dann ab 😞
    _____________________________________
    Wie ich aus der Zeile GC meine gewünschten Zahlen herausbekomme weiß ich nun dank euch.

    Nur wie kann ich in das Programm einbauen, dass die Zeilen vorab geprüft werden ob ein FullGC oder ein GC vorliegt, um dann mit einem passenden Copy(...); darauf zu reagieren.

    int main() 
    { 
        using namespace std; 
        ifstream f1("test.txt"); 
        ofstream f2("backtest.txt"); 
        if( !f1.is_open() || !f2.is_open() ) 
        { 
            cerr << "Fehler beim Oeffnen einer der Dateien" << endl; 
            return -2; 
        } 
    	struc.precision(3);															// 3 Nachkommastelen erzwingen
        struc << fixed;																// Keine expot. Darstellung => Ganzzahl
        copy( istream_iterator< Eintrag >(f1), istream_iterator< Eintrag >(), ostream_iterator< Eintrag >(f2, "\n") );
         return 0; 
    }
    

    Habe mir überlegt, ob ich zwischen Zeile 10 und 14 irgendwie die Zeile Abfrage, dann prüfe und dann mit einer IF-Schleife in den passenden Coppy(..); gehe.
    Aber ich muss das irgendwie immer Zeilenweise machen und nicht die gesammte Datei...hm...

    Hat mir eventuell jm. einen Gedankengang wie ich das umsetzen könnte?

    Herzlichen Dank schon mal
    Fill


Anmelden zum Antworten