Textdatei einlesen und bestimmte Spalten zu bestimmten Zeilen aufsummieren



  • Hallo zusammen,

    Ich muss für eine Aufabe ein kleines Programm mit c++ schreiben.
    Ich habe folgenden Textdateiaufbau:

    Atom 1 Wert
    1 1 5.5
    2 1 4.5
    3 2 3.0
    4 2 3.0
    5 2 3.0
    ...
    Atom 2 Wert
    ...
    Mein Problem ist, dass ich nicht weiß wie ich die Summation in einer bestimmten Spalte zu einem bestimmten Zahleintrag in den entsprechenden Zeilen vornehme, sodass etwa für Atom 1 in diesem oben aufgeführten Beispiel sich der Wert 10 und für Atom 2 der Wert 9 ergibt usw...
    Was ich bisher hinbekommen habe, ist das Schreiben eines Programmes wo alles aufsummiert wird d.h. die gesammten Zahlen in der Datei ohne Unterscheidung von Zeile oder Spalte.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    int main()
    {
    double sum=0;
    double x;
    char fileName[50];
    ifstream inFile;
    cin.getline(fileName,50);
    inFile.open(fileName);
    
    if(!inFile.is_open())
    { 
    cerr <<"konnte nicht geöffnet werden"<<endl;
    exit (1);
    }
    while(inFile>>x)
    
    {
    sum+=x;
    cout<<x <<endl;
    }
    cout<< "\nDie Summe betraegt:\n" << sum <<endl;
    inFile.close();
    return 0;
    }
    

    Ich bin in meinem Wissen, was das Programmieren angeht, noch sehr begrenzt und hoffe auf eure Hilfe. Ich wäre sehr dankbar für jegliche Hilfe und vielen Dank im Voraus.

    lg Dave



  • TurnOver schrieb:

    Mein Problem ist, dass ich nicht weiß wie ich die Summation in einer bestimmten Spalte zu einem bestimmten Zahleintrag in den entsprechenden Zeilen vornehme, sodass etwa für Atom 1 in diesem oben aufgeführten Beispiel sich der Wert 10 und für Atom 2 der Wert 9 ergibt usw...

    Ich habe mal versucht etwas zusammenzuklöppeln, dass im Grunde Deinem Problem entspricht, ohne der Lösung alles vorwegzunehmen.

    Die Idee ist eine map aus der Standardbibliothek zu nehmen, die Atome anhand Ihrer Nummer identifiziert und zu jeder Nummer einen Wert speichert.
    Weiterhin wird die erste Zahl jeder Zeile ignoriert und die letzten beiden als ein Objekt der Klasse delta eingelesen.
    Ein delta besteht aus einem Wert, den ich einem bestimmten Atom hinzuaddieren muss und einer Nummer, die das Atom identifiziert.

    Überraschend wird vielleicht sein, wie einfach, das lesen einer Zeile (und Umwandlung in ein delta ) vonstatten geht (Zeilen 10-13), eine Technik, die Du sicherlich auch in Deiner Lösung so oder ähnlich anwenden kannst.
    Wahrscheinlich ebenso überraschend: die Handhabung der map - die zu erklären ich mir aber spare. (Sieht so einfach aus wie ein Zugriff über einen Index mit [] - ist es auch).
    Selbst ohne die Atome in einer map abzuspeichern und sie quasi im vorbeigehen zusammenzurechnen und die Ausgabe zu machen ist die Aufgabe gut lösbar.

    Übrigens gehe ich von einem vereinfachten Dateiformat aus.
    Meine Datei werte.csv:

    1 1 5.5
    2 1 4.5
    3 2 3.0
    4 2 3.0
    5 2 3.0
    
    #include <fstream>
    #include <iostream>
    #include <map>
    
    struct delta{
      int atom_ix;
      double d;
    };
    
    std::istream& operator>>(std::istream& in, delta& d){
      int ignore;
      return in >> ignore >> d.atom_ix >> d.d;
    }
    
    int main(){
      std::map<int, double> atome;
      const char* filename = "werte.csv";
      std::ifstream infile(filename);
      for(delta d; infile >> d; )
        atome[d.atom_ix] += d.d;
    
      for(const auto& p : atome)
        std::cout << "Atom #" << p.first << ": " << p.second << '\n';
    }
    

    Gutes Gelingen!



  • Hallo Dave,
    Willkommen im C++-Forum.

    Das geht im Prinzip ganz einfach.
    Zunächst musst Du die Zeile mit dem Kommentar überlesen
    und dann jeweils drei Zahlen lesen, von denen Du nur die zweite nutzt - also

    .   inFile.ignore( 999, '\n' );
        double x1, x2, x3;
        for( int zeile=0; zeile<5 && inFile >> x1 >> x2 >> x3; ++zeile) // wenn Anzahl Zeilen =5
        {
            sum+= x2;
        }
    

    Gruß
    Werner



  • inFile.ignore( 999, '\n' );
    

    Soll 999 da einfach eine beliebig große Zahl sein? Wieso nicht std::numeric_limits<std::streamsize>::max() ? ignore ist sogar extra so programmiert dass es für den Wert eine Ausnahme macht.
    Edit: Was erzähl' ich dir da, das weißt du doch alles schon.



  • Hallo und vielen Dank schon mal für die schnellen Antworten euch beiden! Ich habe mal den Vorschlag von Werner in meinen Quellcode eingebunden.
    Das Problem ist aber, dass die gesamte Zeile ausgelesen wird. Ich wollte aber, dass eine bestimmte Zeile gehörend zu einer bestimmten Spalte nicht dazu
    addiert wird sondern als Wert der entsprechenden Zeile abgespeichert und ausgegeben wird sprich:

    1 1 5.5
    2 1 4.5
    3 2 3.0
    4 2 3.0
    5 2 3.0
    

    als Ausgabe:

    Atom 1,1: 10
    Atom 2,2: 9 
    ...
    

    Ich glaube, dass ich hier mit einer "map" arbeiten muss wie schon Furble Wurble angedeutet wurde. Ich weiß nur nicht wie ich das Problem auf meine
    Aufgabenstellung übertragen kann, weil ich den Code nicht so ganz durchschaue. Kann mir den vielleicht jemand erläutern? Oder ist es auch anders möglich?

    lg Dave



  • TurnOver schrieb:

    Ich glaube, dass ich hier mit einer "map" arbeiten muss wie schon Furble Wurble angedeutet wurde. Ich weiß nur nicht wie ich das Problem auf meine
    Aufgabenstellung übertragen kann, weil ich den Code nicht so ganz durchschaue. Kann mir den vielleicht jemand erläutern? Oder ist es auch anders möglich?

    Nein, Du musst sicherlich keine map verwenden.
    Wie ich schon sagte, kannst Du Die Summen auch direkt berechnen und ausgeben

    // blabla wie oben
    int main(){
      const char* filename = "werte.csv";
      std::ifstream infile(filename);
      delta init, act;
      infile >> init;  // lies das erste ein
      while( infile ){
        while( infile>>act && act.atom_ix == init.atom_ix )  // solange zweite Spalte gleich
          init.d += act.d;
        std::cout << "Atom #" << init.atom_ix << ": " << init.d << '\n';
        init = act; // das zuletzt gelesene ist das neue erste
      }
    }
    

    Vorausgesetzt natürlich ich habe das Problem verstanden - ganz klar sind mir Deine Erläuterungen nicht. 😞



  • Nein, du hast mich richtig verstanden. Ich habe es genauso gemeint wie du es schriebst! Vielen Dank schon mal für all deine Bemühungen mir das näher zu bringen! Zwei Fragen hätte ich da noch und zwar: wie sähe es aus wenn ich eine Textdatei habe wie:

    Atom  p    T
    1 1   5.5  1.0
    2 1   4.5  2.0
    3 2   3.0  4.0
    4 2   3.0  4.0
    5 2   3.0  2.0
    

    Und ich möchte etwa ausgeben lassen, dass herauskommt :

    Atom 1,1: p=10,T=3
    Atom 2,2: p=9,T=10
    

    oder ich habe mal keine lust, dass p ausgegeben wird und ignoriere dies, sodass ich mich nur für solch eine Ausgabe entscheide :

    Atom 1,1: T=3
    Atom 2,2: T=10
    

    Ich danke dir nochmals, dass du dir die Zeit nimmst und mir dies erklärst. Ich denke ich habe mehr gelernt als im ganzen Semester im C++ kurs. Danke! 👍



  • TurnOver schrieb:

    Zwei Fragen hätte ich da noch und zwar: wie sähe es aus wenn ich eine Textdatei habe wie:

    Atom  p    T
    1 1   5.5  1.0
    2 1   4.5  2.0
    3 2   3.0  4.0
    4 2   3.0  4.0
    5 2   3.0  2.0
    

    Und ich möchte etwa ausgeben lassen, dass herauskommt :

    Atom 1,1: p=10,T=3
    Atom 2,2: p=9,T=10
    

    Das ist doch das gleiche Problem nur auf zwei Spalten erweitert?!
    Gesetzt Du nimmst eine Klasse wie delta von oben muesstest Du nur die Werte aus der zusätzlichen Spalte noch darin speichern koennen.

    TurnOver schrieb:

    oder ich habe mal keine lust, dass p ausgegeben wird und ignoriere dies

    Wenn Du die Werte ersteinmal richtig akkumuliert hast, ist es doch Deine Entscheidung, welche Du ausgibst und welche nicht.

    TurnOver schrieb:

    Ich danke dir nochmals, dass du dir die Zeit nimmst und mir dies erklärst. Ich denke ich habe mehr gelernt als im ganzen Semester im C++ kurs. Danke! 👍

    Danke für die Blumen 🙂
    Dann beweis es auch und lös die letzten Aufgabe mit den Werkzeugen, die wir Dir an die Hand gegeben haben.

    Ich bin gespannt.



  • struct delta{
      double atom_ix;
      double d;
      double e;
    };
    
      std::istream& operator>>(std::istream& in, delta& d){
      double ignore;
      return in >> ignore >> d.atom_ix >> d.d >> d.e;
    }
    
    int main(){
    char fileName[999];
    ifstream infile;
    cin.getline(fileName,999);
    infile.open(fileName);
    
    if(!infile.is_open()) 
    { 
    cerr <<"Datei konnte nicht geoeffnet werden"<<endl;
    exit (1);
    }
    
      delta init, act;
      infile >> init;  // Das erste wird eingelesen
      while( infile ){
      while( infile>>act && act.atom_ix == init.atom_ix )  
      init.d += act.d;
      init.e += act.e;
      std::cout << "Atom #" << init.atom_ix << ": " << init.d << init.e <<'\n';
      init = act; 
    }
    infile.close();
    }
    

    Ich habe es soweit zum Laufen bringen können bzw. etwas modifiziert so wie ich es haben wollte, aber irgendwie gibt er mir dasselbe aus wie bei dem was wir bis jetzt besprochen haben d.h. die letzte Zeile wird nicht ausgespuckt bzw. aufsummiert, auch wenn keine Probleme beim Kompilieren bestehen.
    sprich:

    Atom  p    T
    1 1   5.5  1.0
    2 1   4.5  2.0
    3 2   3.0  4.0
    4 2   3.0  4.0
    5 2   3.0  2.0
    

    Ich krieg das hier raus: Atom 1,1: 10
    Atom 2,2: 9
    ...
    aber nicht die letzten Zeilen aufsummiert für T.

    Ich darf doch oben

    delta& d

    unverändert lassen? Ich hab leider noch nie mit Operatoren gearbeitet, kann sein, dass ich es auch deshalb nicht durchblicke. Wo mache ich den Fehler? 😞



  • Ähm...ich leide unter gespaltener Persönlichkeit?! *guckt misstrauisch auf's Bierchen*...

    Zu Deinem Problem:
    sieht gut aus, funktioniert auch bei mir.
    Einzig die Zeilen

    while( infile>>act && act.atom_ix == init.atom_ix )  
      init.d += act.d;
      init.e += act.e;
    

    bräuchten noch eine Klammerung, damit beide Inkrementierungen durchgeführt werden.

    Und bei der Ausgabe noch ein Leerzeichen zwischen die Werte?

    Good job!


Log in to reply