Datei zeilenweise einlesen und je nach Spalteninhalt bearbeiten



  • Ich habe aus einem Experimentalphysikpraktikum ein Textfile, dass aus 10000
    Zeilen mit je sechs Spalten besteht, also z.B. so:

    240 13 24 33 29 77
    33 77 98 23 11 24

    Nun muss ich um das Experiment auswerten zu können von allen Zeilen, deren
    erste Spalte die gleiche Zahl enthält die letzten Spaltenelemente nehmen und addieren.

    Dass ich was mit C++ gemacht habe ist jetzt leider schon lang her. Mein
    erster Instinkt wäre jetzt mit getline alle Zeilen auszulesen, zu überprüfen,
    ob sie Zahl a in der ersten Spalte enthält und wenn ja, die letzte Spalte in ein
    array zu stecken.
    Die Frage ist jetzt: wie greife ich mir aus einer Zeile das erste bzw. letzte Element raus?
    Soweit ich getline verstehe, bekommt man da ja eine char-array, ich bräuchte aber eines mit ints.

    Vielen Dank im Voraus!



  • Wenn du sicher bist, daß das Format durchgängig eingehalten wird, kannst du die Zahlen einzeln per Eingabe-Operator einlesen und die Spalten selber mitzählen. Als Alternative würde mir noch einfallen, die Datei zeilenweise per getline() zu lesen und dann (z.B. über einen Stringstream) die Zeile zu zerlegen.

    Edit: Aber bstimmt kommt heute noch ein Kollege mit einer professionellen Lösung, um das auf einen Rutsch zu erledigen 😃



  • CStoll schrieb:

    Edit: Aber bstimmt kommt heute noch ein Kollege mit einer professionellen Lösung, um das auf einen Rutsch zu erledigen 😃

    .. dachtes Du da an jemanden bestimmten? 😉

    Also ich verstehe das so, alle Zahlen in der 6.Spalte addieren, wenn die Zahl in der 1.Spalte einen bestimmten Wert a hat. Angenommen die Zahlen sind immer Integer, dann ginge das z.B. so:

    #include <iostream>
    #include <fstream>
    #include <limits> // numeric_limits<>
    
    int main()
    {
        using namespace std;
        const int a = 33;
        int sum = 0;
        ifstream file( "input.txt" ); // Ausgabe des Experiments
        if( !file.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -1;
        }
        for( int zahl1; file >> zahl1; )
        {
            if( zahl1 == a )
            {
                int zahl6;
                for( int sp = 1; sp < 6 && file >> zahl6; ++sp )
                    ;
                if( file )
                    sum += zahl6;
            }
            else
                file.ignore( numeric_limits< streamsize >::max(), '\n' );
        }
        cout << "Summe = " << sum << endl;
        return 0;
    }
    

    sieht aber irgendwie ein bisschen langweilig aus. Abstrahieren ich mal ein wenig:
    Es soll addiert werden, also std::accumulate. Aber nicht jedes Element, sondern nur bestimmte, also boost::filter_iterator. Einzulesen wären immer eine Zeile bestehend aus 6 Zahlen, also eigenes Objekt. Ergo:

    #include <iostream>
    #include <fstream>
    #include <iterator> // istream_iterator<>
    #include <numeric> // accumulate
    #include <boost/iterator/filter_iterator.hpp>
    #include <boost/bind.hpp>
    
    struct Line
    {
        friend std::istream& operator>>( std::istream& in, Line& l )
        {
            in >> l.m_zahl1;
            for( int sp=1; sp<6 && in >> l.m_zahl6; ++sp )
                ;
            return in;
        }
        bool Fits( int a ) const {  return a == m_zahl1; } // passt, falls zahl1 == a
        friend int operator+( int sum, const Line& l ) { return sum + l.m_zahl6; } // zum Addieren
    private:
        int m_zahl1, m_zahl6;
    };
    
    int main()
    {
        using namespace std;
        using namespace boost;
        const int a = 33;
        ifstream file( "input.txt" ); // Ausgabe des Experiments
        if( !file.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -1;
        }
    
        cout << "Summe = " << accumulate( make_filter_iterator( bind( &Line::Fits, _1, a ), istream_iterator< Line >( file ) ),
            make_filter_iterator( bind( &Line::Fits, _1, a ), istream_iterator< Line >() ), 0 )<< endl;
        return 0;
    }
    

    Gruß
    Werner



  • Werner Salomon schrieb:

    CStoll schrieb:

    Edit: Aber bstimmt kommt heute noch ein Kollege mit einer professionellen Lösung, um das auf einen Rutsch zu erledigen 😃

    .. dachtes Du da an jemanden bestimmten? 😉

    Wenn du schon so direkt fragst... 🕶



  • Wie schön dass die verbesserte Version komplexer, länger, imperformanter ist und sogar noch 3rd party Libs dazuzieht. 😃



  • Ethon schrieb:

    Wie schön dass die verbesserte Version komplexer, länger, imperformanter ist und sogar noch 3rd party Libs dazuzieht. 😃

    imperformanter ? Hast Du es gemessen?



  • Ethon schrieb:

    Wie schön dass die verbesserte Version ....

    Um nicht missverstanden zu werden. Die zweite Version erhebt keinen Anspruch darauf, die bessere oder gar schnellere Version zu sein; obwohl sie es vielleicht ist. Ich habe beim Nachmessen schon so manche Überraschung erlebt.
    Es ist die coolere Version 🕶

    Allen Ernstes: Unsere Management hat uns aufgefordert coole Software zu schreiben.



  • Werner Salomon schrieb:

    Es ist die coolere Version 🕶

    Allen Ernstes: Unsere Management hat uns aufgefordert coole Software zu schreiben.

    Damit es keiner debuggen kann?



  • Werner Salomon schrieb:

    Allen Ernstes: Unsere Management hat uns aufgefordert coole Software zu schreiben.

    Aber der Sourcecode war ihnen dabei bestimmt völlig egal 😉



  • Mit entsprechendem Bibliotheksunterbau kann man das aber auch lesbarer machen.
    Sofern keine Fehlerbehandlung nötig ist, würde ich das z.B. so lösen:

    int main()
    {
      const int a=42;
      TFileStream file("input.txt");
      int sum=0;
      foreach(line,file)
      {
        auto parts=split(line,' ');
        if (StrToInt(parts[0])==a)sum+=StrToInt(parts.back());
      }
      cout << "Summe: " << sum << endl;
    }
    

Log in to reply