Zahlen aus Datei auslesen



  • Hallo liebe C++-Gemeinde,

    ich möchte gerne aus einer Datei nur Zahlen auslesen um diese weiterverwenden zu können. Es gibt ifstream zum Einlesen, die Sache ist nur, der Text enthält auch andere Zeichen als Zahlen. Wie kann ich die bestimmten Zeilen die ich auslese ansprechen, wie z.B. eine Zeile überspringen und in der dritten weitermachen, und wie könnte ich z.B. in einer Zeile nur die ersten 4 Zahlen auslesen und dann in die nächste Zeile springen und dort die ersten 3 Zahlen z.B. auslesen und dabei die anderen Zeichen ausser Acht lassen, bis ich das Ende der Datei erreicht habe?

    Lieben Dank an euch,

    Laura



  • Hallo Laura,

    willkommen im C++-Forum.

    Zeichen überlesen kann man mit der Methode istream::ignore. Da Du das wohl öfter benötigst, kann man das auch in einen sogemannten Manipulator (hier skipline) packen, der jeweils alles bis zum Zeilenende überliest. Anschließend wird dieser so aufrufen, wie das Einlesen einer Variablen.

    Mal angenommen, die Datei sieht so aus:

    erste Zeile
    zweite Zeile
    10 20 30 da steht noch was
    44 55 .. egal
    

    dann liest folgender Code die Zahlen ein:

    #include <iostream>
    #include <fstream>
    #include <limits>
    
    std::istream& skipline( std::istream& in )
    {
        return in.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
    }
    
    int main()
    {
        using namespace std;
        ifstream datei("input.txt");
        if( !datei.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -1;
        }
        int z1, z2, z3;
        int z4, z5;
        if( datei >> skipline >> skipline >> z1 >> z2 >> z3 >> skipline >> z4 >> z5 )
        {
            // alles ohne Fehler gelesen
            cout << "Die 5. Zahl ist " << z5 << endl;
        }
        return 0;
    }
    

    Die Ausgabe wäre dann:

    Die 5. Zahl ist 55
    

    Gruß Werner



  • Hallo Werner,

    lieben Dank für deine Begrüssung und deine Antwort.
    In dem Beispiel stehen die Zahlen am Anfang. Was aber wenn die Zahlen irgendwo nach anderen Zeichen und Leerzeichen in der Zeile stehen? (Zählen Leerzeichen auch als Zeichen?) Und wie kann man das Auslesen rekursiv gestalten?

    z.B.:

    nullte Zeile 10 40
    erste zeile
    zweite zeile
    dritte zeile 10 20 30 70 
    vierte zeile 6 7 8 90
    ...
    Ende
    

    Wie kann ich z.B. in der nullten zeile die 10 und die 40 auslesen und die erste und zweite zeile überspringen (skipline) und danach rekursiv die ersten drei zeichen auslesen bis zur letzten Zeile wo Ende steht?
    Und was bedeutet das numeric_limits ...?

    Danke Werner



  • Laura G. schrieb:

    Was aber wenn die Zahlen irgendwo nach anderen Zeichen und Leerzeichen in der Zeile stehen?

    Hallo Laura,

    Du musst natürlich wissen, wie die Datei genau aussieht. Entweder beginnen die Zahlen ab einer bestimmter Spalte oder hinter einem eindeutigen Zeichen. In beiden Fällen kann man mit ignore den Vorspann in der Zeile überspringen.

    Laura G. schrieb:

    Zählen Leerzeichen auch als Zeichen?

    bei der Methode ignore schon, da ignore unformatiert einliest. Beim Formatierten Einlesen zählen Leereichen (bzw. alle White Space Character) nicht mit.

    Laura G. schrieb:

    Und wie kann man das Auslesen rekursiv gestalten?

    Ich nehme mal an, Du meinst mit 'rekursiv' in einer Schleife.

    Wenn die Datei so aussieht

    nullte Zeile 10 40
    erste zeile
    zweite zeile
    dritte zeile 10 20 30 70 
    vierte zeile 6 7 8 90
    ...
    Ende
    

    liest folgendes Programm die Zahlen ein:

    #include <iostream>
    #include <fstream>
    #include <limits>
    
    std::istream& skipline( std::istream& in )
    {
        return in.ignore( std::numeric_limits< std::streamsize >::max(), '\n' );
    }
    
    int main()
    {
        using namespace std;
        ifstream datei("input.txt");
        if( !datei.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -1;
        }
        int z1, z2;
        datei.ignore( 13 ) >> z1 >> z2 >> skipline >> skipline >> skipline;
        for( int zi, zi1; datei.ignore( 13 ) >> zi >> zi1 >> skipline; ) // das skipline springt an den Anfang der folgenden Zeile 
        {
            cout << ": " << zi << " " << zi1 << endl;
        }
        if( !datei.eof() )
            cerr << "Fehler beim Lesen der Datei" << endl;
        return 0;
    }
    

    Ich nehme dort an, dass die Zahlen jeweils in der 14.Spalte beginnen.
    In Zeile 20 siehst Du 3 skiplines. Das ist notwendig, da sich der Lesezeiger nach der letzten Zahl immer noch in der ersten Zeile befindet. Diese muss genau so übersprungen werden, wie die folgenden zwei Zeilen.

    Laura G. schrieb:

    Und was bedeutet das numeric_limits ...?

    Das ist ein Template-Konstrukt, welches zu den einzelnen Typen (hier std::streamsize) die Eigenschaften liefert - z.B. die größte (max) und kleinste (min) mögliche Zahl, die mit diesem Typ dargestellt werden kann (s. numeric_limits).
    Der erste Parameter bei ignore gibt an, wieviele Zeichen überlesen werden sollen. Der Parameter ist vom Typ std::streamsize. Gibt man hier numeric_limits<streamsize>::max() an, so bedeutet das soviel wie unendlich. d.h. das Überlesen stoppt erst beim gefundenen Zeichen (hier '\n') oder am Dateiende.

    Gruß
    Werner



  • Falls du einfach alle Zahlen herausfiltern willst, sollte sowas funktionieren:

    #include <fstream>
    #include <iostream>
    
    int main() {
    	std::ifstream Stream( "input.txt" );
    	while ( Stream ) {
    		if ( isdigit( Stream.peek() ) ) {
    			// Ziffer gefunden, es kommt eine Zahl
    			int Number;
    			Stream >> Number;
    			std::cout << Number << '\n';
    		}
    		else {
    			Stream.get();
    		}
    	}
    }
    

    Stream.peek() lässt dich dabei das nächste Zeichen anschauen, ohne dass es dabei verworfen wird.

    Rekursivität brauchst du hier nicht, oder besteht da ein Prof/Dozent darauf?



  • Ich würde die Aufgaben eher trennen, da die Zeilen doch sehr unterschiedliche Formate haben (mal mit, mal ohne Text). Ein starres Parsen bereits beim Einlesen ist da eher unflexibel. Motto: Jede Funktion nur eine Aufgabe, und die richtig. Also:

    1. Einlesen der Zeile
    2. Parsen aller Zahlen darin (egal ob keine oder viele)

    Zum Einlesen geht getline (achtung, da gibt es zwei Versionen, ich meine diese: http://www.cplusplus.com/reference/string/getline/). Danach hat man die ganze Zeile in einem String.

    Dann nimmt man sich in einer anderen Funktion den String vor und sucht alle Zahlen (z.B. von Space zu Space hüpfen und schauen, ob es eine Zahl ist.).



  • minastaros schrieb:

    Motto: Jede Funktion nur eine Aufgabe, und die richtig. Also:

    1. Einlesen der Zeile
    2. Parsen aller Zahlen darin (egal ob keine oder viele)

    Zum Einlesen geht getline .. Danach hat man die ganze Zeile in einem String.

    Dann nimmt man sich in einer anderen Funktion den String vor und sucht alle Zahlen (z.B. von Space zu Space hüpfen und schauen, ob es eine Zahl ist.).

    Hallo minastaros,

    nein - bitte nicht 😞 . Das Einlesen der Zeichen übernimmt bereits der unter dem ifstream liegende std::filebuf und das Parsen macht der istream. Und wenn der istream beim Parsen nicht reicht, so kann man ihn mit Manipulatoren oder dergleichen aufbohren. Damit lassen sich 99% aller Einleseprobleme lösen.

    Der vorschnelle Gebrauch von getline macht mehr Probleme als es vordergründig löst. Suche doch hier mal im Forum nach 'getline ist doof' 😃

    Gruß
    Werner



  • Hallo zusammen,

    lieben Dank euch allen für eure Antworten.

    @Werner: Wieso komme ich am Ende immer zu der Ausgabe: Fehler beim Lesen der Datei? Heisst das er erkennt das Ende der datei ( datei.eof() ) nicht? Falls in der letzten Zeile ein "?" stehen würde, das für das Ende der Datei steht, könnte man das damit irgendwie abfangen?

    Lieben Gruß,

    Laura



  • Laura G. schrieb:

    @Werner: Wieso komme ich am Ende immer zu der Ausgabe: Fehler beim Lesen der Datei? Heisst das er erkennt das Ende der datei ( datei.eof() ) nicht? Falls in der letzten Zeile ein "?" stehen würde, das für das Ende der Datei steht, könnte man das damit irgendwie abfangen?

    Hallo Laura,

    wie schon gesagt, Du musst schon wissen, wie Deine Datei formatiert ist - ich weiß es nicht! Bei dem von mir gepostetem Code bin ich davon ausgegangen, dass maximal noch eine Zeile hinter der letzten Zahl steht und die Datei dann zu Ende ist. Das scheint nicht der Fall zu sein.

    Bei der Fehlerkontrolle sollte man ein falsches Dateiformat erkennen. Ansonsten könntest Du auch einen Code verwenden, wie ihn fdgdfg vorgeschlagen hat. Der liest einfach alle Zahlen ein. Falls es aber Formatierungsfehler in der Datei gibt, so wird dies nicht erkannt.

    Wie sieht denn das Dateienden - bzw. das was hinter der letzten Zahl folgt - aus? Oder weißt Du im Vorfeld, wieviele Zeilen mit Zahlen in der Datei stehen müssten?



  • Hallo zusammen,

    @Werner: Hinter der letzten Zahl kommt nur noch eine Zeile mit einem ? (Fragezeichen) und dann ist die Datei zu Ende.

    Danke euch allen.

    Lieben Gruß,

    Laura



  • Werner Salomon schrieb:

    so kann man ihn mit Manipulatoren oder dergleichen aufbohren. Damit lassen sich 99% aller Einleseprobleme lösen.

    Der vorschnelle Gebrauch von getline macht mehr Probleme als es vordergründig löst. Suche doch hier mal im Forum nach 'getline ist doof' 😃

    Gruß
    Werner

    Hallo Werner,
    es gibt immer mehrere Wege, wie man es machen kann. Es ist schon OK, über andere Wege auch nachzudenken, nur sehe ich auch kein Problem darin, Dateien zeilenweise zu verarbeiten, zumindest, wenn die Anordnung in Zeilen wie hier beabsichtigt zu sein scheint. Mag getline auch doof sein - bislang hatte ich damit keine Probleme. Je nach Anwendung eben.

    Bei Deinem Ansatz - der sicher auf seine Weise elegant ist - sehe ich das Problem, dass das ignore( 13 ) eine magic number einbindet, die schon bei "dreiundzwanzigste Zeile" nicht mehr funktioniert, oder?

    Womit ich mich anfreunden könnte, wäre ein Manipulator, der einfach allen Text und Whitespaces ignoriert - sofern es das Dateiformat erlaubt.

    Daher Frage an Laura:
    - Willst Du immer bestimmte Zeilen überspringen, d.h. ist es Teil des Dateiformats, dass z.B. in Zeile 0 immer 2 Zahlen stehen und in Zeile 3 vier? Haben diese Zahlen eine unterschiedliche Bedeutung?
    - Oder ist die Anzahl der Zahlen je Zeile beliebig?
    - Ist der Text eher zufällig verteilt, oder ist zumindest dessen Position in bestimmten Zeilen immer gleich?

    Worauf ich hinaus will: Willst Du nur einfach alle Zahlen der Reihe nach herausfiltern oder gehören Zeilen und Texte mit zum festen Protokoll, die man im Zusammenhang mit den Zahlen beachten muss?


Anmelden zum Antworten