Möglchst einfach von Konsole einlesen



  • Hallo zusammen

    Mein Problem: ich kann zwar die gestellte Aufgabe lösen, aber - da ich C++ noch nicht so gut kenne - kenne ich offenbar die Funktion nicht, die Testdaten einzulesen.

    Diese sollen von cin gelesen werden und sehen z.B. so aus:

    3
    1 3 2 1
    1 2 22 1
    19 38 1 -2
    

    D.h. in der ersten Zeile steht, wie viele Zeilen folgen werden, anschliessend folgen jeweils 4 mit Leerschlag getrennte Zahlen pro Zeile, die vorzugsweise in ein Array oder so gespeichert werden.

    Wie mache ich das am besten? Gibt es eine solche Funktion (Split, oder was ähliches wie sprintf zum lesen)?

    Vielen Dank für eure Hilfe.

    Ich wünsche ausdrücklich keine Komplettlösung, sondern wie ich das am effizientesten mache (da ich das das ganze Semester wohl noch einige Male machen werde.... :))

    Viele Grüsse

    Lukas



  • Nein. Sehr einfach mit std::cin:

    #include <iostream>
    #include <vector>
    #include <string>
    
    int main()
    {
        std::vector<int> zahlen(16);
        for(int a(0);a < 16;++a)
            std::cin >> zahlen.at(a);
    }
    

    Liest genau 16 Zahlen ein (4 * 4). Kannst aber auch in einer Zeile 3 und in der anderen 5 stehen haben oder 15 in eine und 1 in die andere. Macht keinen Unterschied. 😉

    Edit: Tut mir Leid, Komplett-Lösung. Soll ich es dir erklären?



  • -Was willst du mit den Zahlen machen? Willst du damit rechnen?
    -cin ist im Allgemeinen an die Tastatur gekoppelt. Stehen die Daten in einer Datei? Dann musst du ein ifstream-Objekt nehmen, und nicht cin.


  • Mod

    Gugelmoser schrieb:

    -cin ist im Allgemeinen an die Tastatur gekoppelt.

    cin ist im Allgemeinen an die Standardeingabe gekoppelt. Das kann alles mögliche sein. Umleitung der Standardeingabe ist das 1x1 des Workflows auf der Konsole.

    @Threadersteller:
    1. Ist das Format so vorgegeben oder hast du dir das selber als besonders leicht einlesbar ausgedacht? Denn das ist es nicht. Wenn du die führende 3 weglassen wurdest, wäre alles viel einfacher, sowohl für den Leser der Daten und ganz besonders für den Ersteller, der dann einfach Zeilen einfügen kann, ohne sich Sorgen zu machen, an einer völlig anderen Stelle etwas ändern zu müssen.
    2. Ist das alles was eingegeben wird, oder kommt danach noch was?
    3. Soll auf Fehler geprüft werden, so dass zum Beispiel nicht 3 Zahlen in einer Zeile stehen und in der nächsten 5? Oder reicht es dir, einfach Zahlen in Viererpaketen einzulesen, egal wie sie genau formatiert sind?
    4. Du wirst sicherlich eine Klasse oder Struct für die Daten haben. Die übliche Vorgehensweise wäre, den Operator>>(istream &, DeineKlasse&) zu überladen. Das klingt kompliziert, ist aber ganz einfach. Wenn du diese Fragen beantwortest, kann ich oder jemand anderes dir dies konkret an deinem Fall zeigen.



  • Hallo

    Ach so, vector-Klasse habe ich noch nicht gekannt, wenn man das denkt ist es dann gar nicht mehr so schwer 😉

    @Gugelmoser: Was ich mit den Zahlen machen will? Naja, ist ein Algorithmus, aber den hab ich schon, und ja ich denke, ich muss cin verwenden, das ganze muss ich online einreichen und wird mit Testeingaben online ausgewertet, und das funktioniert mit stdin und stdout, steht zumindest auf der Vorlesungsseite 🙂

    @SeppJ: 1. Nein, ist es nicht, das ist genau so vorgegeben 🙂
    2. Das ist alles das eingegeben wird
    3. Sehe ich dann, ob das so geht.
    4. Nein, das ist alles was ich dazu habe, es ghet nicht um Programmieren in der Vorlesung, sondern um Algorithmen und Datenstrukturen.

    Vielen Dank euch. Sollte so klappen 🙂


  • Mod

    LukasM schrieb:

    4. Nein, das ist alles was ich dazu habe, es ghet nicht um Programmieren in der Vorlesung, sondern um Algorithmen und Datenstrukturen.

    Nun, da hatte ich das doch gerade erwartet, dass die Daten in irgendeiner Struktur landen, wenn sie strukturiert eingelesen werden.

    Wie auch immer, eigentlich würde man eine unbekannte Anzahl von Integern zum Beispiel so einlesen:

    vector <int> zahlen;
    {             // Scope auf, weil die Variable zahl nur temporär ist
     int zahl;
     while (cin >> zahl)  // Solange Lesevorgang gut ging...
      zahlen.push_back(zahl);   // ...den gelesenen Wert abspeichern
    }             // Lesen fertig, Scope zu, zahl tot
    

    Mit der Vorgabe des etwas unglücklichen Formats wird das etwas komplizierter ( ⚠ ungetestet ⚠ ):

    vector <int> werte;
    {
     int anzahl_zeilen;
     cin >> anzahl_zeilen;
     string zeile;
     int zeilenzaehler;
     for (zeilenzaehler = 0; 
          zeilenzaehler < anzahl_zeilen and getline(cin, zeile);  // Wenn noch eine Zeile zu lesen ist, so wird diese gelesen. Falls das Lesen scheitert, bricht die Schleife ab
          ++zeilezaehler
         )
      {
        // Lesen erfolgreich, verarbeite Zeile. Packe sie dazu in einen Stream:
        stringstream zeilenparser(zeile);
        // Und lese diesen aus:
        int spaltenzaehler;
        int wert;
        for (spaltenzaehler = 0; 
             spaltenzaehler < 4 and zeilenparser >> wert;
             ++spaltenzaehler;
            )
         werte.push_back(wert);
    
        // Werte der Zeile gelesen. Waren es auch wirklich 4? Und kommt im Rest der Zeile auch nichts anderes mehr?
        char testchar;
        if (zeilenzahler != 4 or zeilenparser >> testchar)
         throw runtime_error("Anzahl der Spalten stimmt nicht!");
      }
     // Zeilen gelesen.  Waren es auch wirklich anzahl_zeilen? Und kommt im Rest der Eingabe auch nichts anderes mehr?
     char testchar;
     if (zeilenzahler != anzahl_zeilen or cin >> testchar)
      throw runtime_error("Anzahl der Zeilen stimmt nicht!");
    }  // Scope zu, das Ende der temporären Variablen
    // An dieser Stelle sollten anzahl_zeilen mal 4 Werte in werte stehen.
    

    Du siehst, das kann ganz schön umständlich werden. Wenn man es ein bisschen schöner machen möchte, kann man auch diese Logik in die Einleseroutinen selbst auslagern. Ein erster Schritt dazu, wäre die Operatorüberladung, aber dazu sollte man erst einmal eine sinnvolle Datenstruktur definieren. Dann überlädt man den Operator für diese Struktur. Die Definition der Operatorfunktion sähe dann ungefähr so aus wie der gezeigte Code, man würde dann eben nicht in werte schreiben, sondern in die Struktur. Dann sähe das Einlesen im Hauptprogramm auch wieder so ähnlich aus, wie mein erstes Beispiel. Ein zweiter Schritt wäre, das Verhalten des Streams an sich zu ändern, so dass dieser die Trennung der Werte so vornimmt, wie es dem vorgegebenen Format entgegenkommt. Dann wäre auch das Einlesen nicht mehr so umständlich mit dem Zwischenschritt über den Stringstream. Das ist dann aber wirklich kein Anfängerthema mehr, das erkläre ich jetzt mal nicht.



  • Hallo

    Naja, so weit sind wir noch nicht 🙂 Wie gesagt es ist unsere erste derartige Vorlesungen und es geht eigentlich nur drum, einen Algorithmus (gegeben als Rekursionsgleichung) möglichst effizient zu programmieren und eine gewisse Laufzeit einzuhalten 🙂

    Die Version von Hacker hat so auf jeden Fall super geklappt und genügt wohl vollends, weil dies nicht das Einlesen nicht die eigentliche Problemstellung ist (zumindest nicht im Moment). Zumindest hat das Hard-Testset keine Fehler ausgegeben 🙂

    Nochmals danke für eure Hilfe!

    Viele Grüsse

    Lukas


Anmelden zum Antworten