Hilfe bei der Ausgabe in eine .txt datei.



  • Danke für die schnelle Antwort.

    Funktionen kenne ich nicht.
    Wir habe nur ein semester C++, danach brauche ich es nur noch für evtl CNC Maschinen programierung.



  • rellik schrieb:

    /*grusel*/
    

    Wie die Ausgabe in die Datei auszusehen hat, hat HighLigerBiMBam ja schon erklärt. Seekp brauchst du dafür natürlich nicht, wenn du ios::app verwendest.

    Zum Grundsätzlichen:
    - wie schon erwähnt gilt goto als schlechter Stil, und das nicht von ungefähr. Funktionen und Schleifen machen den Code einiges übersichtlicher.
    - printf wurde ja auch schon erwähnt, dass das nicht mit cout gemsicht werden sollte. Ein Grund: printf ist nicht typsicher, in den Formatflags (%s, %d und wie sie alle heißen) daher anfällig für Vertipper und kann mit allem, was über die eingebauten primitiven Datentypen hinausgeht nichts anfangen. Ein Relikt aus C-Zeiten, das eine ordentliche Ablösung verdient und bekommen hat. In C++ sollte es wenn überhaupt dann nur an performancekritischen Stellen eingesetzt werden. (Wobei man an solchen Stellen lieber ganz auf Ausgaben verzichtet)

    - <stdio.h> und <time.h> gibts in C++ nicht, die heißen jetzt neuerdings (seit 12 Jahren) <cstdio> und <ctime>

    - in den Anfängen würde ich mich an deiner Stelle erstmal auf reines C++ beschränken, die WinAPI-Spielereien mit bunter Konsole sind zwar spaßig, blähen den Code aber nur auf und bringen keinerlei Lerneffekt. (Ist aber Geschmackssache)
    - auch wenn du mit der locale Umlaute anzeigbar machen kannst, gehören sie nicht zum Zeichensatz, den ein Copiler verarbeiten können muss. Der Code den du da hast ist also nicht portabel. Das gehört auch zur Kategorie "Spielerei ohne Lerneffekt"
    - die Semikola hinter den case-labels (und dem goto-label) sind sinnlos und können wegbleiben.
    - Variablen können und sollten in C++ dort deklariert werden, wo sie auch gebraucht werden. das erleichtert das spätere Lesen des Codes, damit man nciht 3 Seiten scrollen muss, um rauszufinden welchen Typ Variable X nochmal hatte.
    - Variablennamen wie i und p sind schlecht - sie sagen so garnichts drüber aus, was i und p denn sind. Ausnahmen sind Schleifenzähler, die sollten dann aber im Schleifenkof deklariert und definiert werden
    - in C++ gibts den Typ bool. Deine Variable abbruch ist vermutlich dazu da, eine Abbruchbedingung anzuzeigen, also 1 oder 0. Für sowas sind bools da.
    - einige deiner Kommentar sind völlig überflüssig, z.B. wenn du in einer Zeile beschreibst, was genau die eine Zeile tut. Normalerweise sollte der Code einer Zeile aussagekräftig genug sein. Vor allem kommen dann nicht solche komischen Kommentare wie in Zeile 44-46 zustande

    Wie ich das Programm auf den ersten Wurf umstellen würde:

    #include <iostream>
    Ist für die Eingabe und Ausgabe
    #include <cstdio>                                                       
    #include <windows.h>                                                    
    #include <cstdlib>                                                      
    #include <ctime>           
    #include <fstream>
    
    using namespace std;
    
    void printWelcomeScreen()
    {
      time_t now = time(0);
      cout <<"\n\n\n\n\n\n\n\n\n                            ";
      cout << ctime(&now);
      cout << "                ";
      cout << "     Das Programm berechnet Kraefte am Traeger     \n";
      cout << "                ";
      cout << "   Erstellt von Daniel Knote und Robbin Janson   \n";
      cout << "              ";
      cout << "Projekt C++ vom Wintersemester Maschinenbau 2010/2011\n";
    
      Sleep(4000);                                                            // Ende des Anzeigebildschirmes mit       
    }
    
    void printHelpScreen()
    {
      cout << "\n\n\n\n\n\n\n\n\n                     Damit das Programm richtig rechnen kann,                   \n"
              "        muessen alle Werte mit dem richtigen Vorzeichen engegeben werden.        \n"
              "      Die Laengen müssen in Meter und Die Kraefte in Newton angegeben werden.     "
              "                   Die Lagerkraefte werden immer mitgezaehlt";
      Sleep(9000);       
    }
    
    void berechnungEinzelkraft()
    {
      int anzahlkraefte;
      cout << "Wieviele Kraefte haben Sie? : "; 
      cin >> anzahlkraefte;
      cout << "\nGeben Sie nun die Kraefte an. Lagerkraefte werden nicht gesondert behandelt :\n";
    
      float ergebnis = 0;
      for(int i = 0; i < anzahlkraefte; i++)
      {  
        float kraft_n;
        cout <<"Geben Sie Kraft Nummer " << i + 1 << " ein : ";
        cin >> kraft_n;
        ergebnis += kraft_n;
      }
    
      ergebnis *= -1;
      cout << "\nDie fehlende Kraft betraegt: " << ergebnis << " kN";               
      //usw.
    }
    
    char menue()
    {
      system("cls");  
      cout << "                      ---------------Menue---------------\n\n";      
      cout << "              -1-   Wichtiges zum richtigen Berechnen\n";
      cout << "              -2-   Eine einzelne Kraft ( Summe aller Kraefte )\n";
      cout << "              -3-   Eine fehlende Laenge \n";
      cout << "              -4-   Die Lagerkraefte\n";
      cout << "              -5-   Maximales Biegemoment und wo es auftritt\n";
    
      cout << "\n\n                    Ihre Auswahl : "; 
      char auswahl;
      cin >> auswahl;
    
      auswahl = 1;
    
      return auswahl;
    
    }
    
    int main()                                                                // Beginn des Programms
    {   
      printWelcomeScreen();
      bool abbruch = false;
      do
      {
        char menueauswahl = menue();
        switch(menueauswahl)
        {   
        case '1':
          printHelpScreen();
          break;
    
        case '2': 
          berechnungEinzelkraft();
          break;
    
        case 'q':
          abbruch=true;
          break;
    
        default:
          cout << "Falsche Eingabe!\n";
          Sleep(4000);
          system("cls");
        } //end switch
      } while (!abbruch);
    }
    

    Ist wie gesagt nur ein erster Wurf, danach gibts noch einiges zu verschönern 😉

    rellik schrieb:

    Funktionen kenne ich nicht.
    Wir habe nur ein semester C++, danach brauche ich es nur noch für evtl CNC Maschinen programierung.

    Für alles was mehr als ein "Hello World" werden soll braucht man Funktionen.



  • Ok, nun habe ich einiges verstanden. Sieht auch viel übersichtlicher aus.
    Werde das alles mal so zusammensetzen wie ich es brauche mit dem Programm und dann nochmal posten.

    Das mit dem bool habe ich aber noch nicht verstanden.

    Danke



  • Bool ist ein C++-Datentyp, der einen Wahrheitswert speichern kann, also entweder true oder false. Er ist aber nicht nur ein Bit groß, sondern ein Byte (da er sonst nicht adressiert werden könnte.



  • void berechnen_einzelne_kraft()
    {
    					  ergebnis_1 = 0;											
    					  zähler_1 +=  1;												
    			cout << "Wieviel Kräfte haben Sie? : "; cin >> anzahlkraefte;		
    			cout << "\nGeben Sie nun die Kräfte an.\n";							
    
    			 for(i = 0; i < anzahlkraefte; i++)									
    			 {   
    			  cout <<"Geben Sie Kraft Nummer " << i + 1 << " ein : ";cin >> kraefte[i]; 			 
    			  ergebnis_1 += kraefte[i];	
    			  if (i<1)																
    				{																		
    				ofstream outfile;														
    				outfile.open ("Kräfteausgabe.txt", ios::app); 
    				outfile << "Einzelne Kraft\n";
    				outfile << "Die Daten der Berechnung "<<zähler_1<<"  : ";
    				outfile.close();  
    				}
    			 ofstream outfile;															
    			 outfile.open ("Kräfteausgabe.txt", ios::app);								
    			 outfile << " | "<<kraefte[i]<<" ";
    			 outfile.close(); 
    			 }																			 
    			 ergebnis_1 *= -1; 	
    
    			 ofstream outfile;															// Schreiben des Ergebnisses
    			 outfile.open ("Kräfteausgabe.txt", ios::app); 
    			 outfile << "\nDie berechnete fehlende Kraft beträgt : "<< ergebnis_1 <<" N\n\n" ;
    			 outfile.close(); 
    
    			 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),BACKGROUND_INTENSITY|BACKGROUND_RED); 
    			 cout << "\nDie fehlende Kraft betraegt: " << ergebnis_1 << " kN\n\n";							   			
    			 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);	
    			Sleep(3000);					
    }
    
    int main() 
    
    {  
    setlocale(LC_ALL,"GERMAN");	
    
      willkommens_bildschirm();
      bool abbruch = false;
      do
      {
      char menueauswahl = menue();
        switch(menueauswahl) 
        { 
    
        case '1':
          hilfe_bildschirm ();
          break;  
    
        case '2':		
          berechnen_einzelne_kraft();		  
          break;
    
    	case '3':
          berechnung_lagerkraefte();
          break;
    
    	case 'q':
          abbruch=true;
          break;  
    
        default:
          cout << "\n\nFalsche Eingabe!\n";
          cout << "Bitte erneut versuchen";
    	  Sleep(3000);	
    	}
    }while (!abbruch); 
    }
    

    Bevor ich mich nun ranmache das ganze zusammen zu bauen (sind insgesamt 9 einzelne Berechnungen 😉 ), habe ich noch Fragen.

    Ist es möglich, das wenn die Zeilen 56 - 58 (case 2) ausgeführt worden sind, man da eine Auswahl hinmacht wo man auswählen kann ob man zu 56 oder zu 48 springt.

    Wie ich das mit goto machen würde weiß ich ja, aber das ist ja nicht der richtige Weg 😉

    Wenn ich die Berechnung in 56 - 58 gemacht habe, kommt das Ergebnis, das ich mir mit sleep ein paar sekunden anschauen kann. Gibt es ein Befehl, der eine Pause einbaut und erst bei drücken von Enter fortfährt?

    Ich danke Euch schonmal.



  • Ist es möglich, das wenn die Zeilen 56 - 58 (case 2) ausgeführt worden sind, man da eine Auswahl hinmacht wo man auswählen kann ob man zu 56 oder zu 48 springt.

    Was genau meinst Du, eine Auswahl, ob man die gleiche Berechnung nochmal durchführen will?

    Du solltest übrigens die Sleep( ) ebenso weglassen - auf der Console kommt immer gleich die nächste Ausgabe. Und niemand wartet gern.

    Versuche getchar( ) .



  • Getchar() funktioniert nicht. mit getch() klappt es.

    Ja, ich meine das man entweder die selbe Berechnung nochmal macht oder zum Auswahlbiledschirm gelangt und eine Andere berechnet. Das man das über ein kleines Menü macht. Auswahl 1 oder 2.

    Das Menü ist nicht das Problem, nur wie ich es dann umsetze, das weiß ich nicht, da es ja in mehreren "case" vorkommt.



  • Es gibt viele Wege. Ein eleganter wäre folgender: Du trennst die Auswahl des später zu verwendenden Algorithmus' (also Lagerkräfte, einelne Kraft etc.) von der eigentlichen Berechnung. Warum? Weil Du eine Art Steuerkonstrukt dazwischenschalten willst, nämlich die Steuerung, ob der Algorithmus einmal oder mehrmals aufgerufen werden soll.

    Zunächst lese bitte etwas über Funktionspointer.

    Dazu definierst Du einen neuen Typ, nämlich pointer auf eine Funktion, die void nimmt und void zurückgibt:

    typedef void(*berechnung_t)(void);
    

    Dann hast Du Deine Berechnungen wie gehabt. Die Signatur dieser Funktionen passt zum Funktionspointer-Typ:

    void berechnen_einzelne_kraft()     // <-- ist eine void-Funktion, die void zurückgibt
    {
    	cout << "einzeln" << endl;
    }
    
    void berechnung_lagerkraefte()           // <-- ebenso
    {
    	cout << "lager" << endl;
    }
    

    Das Wiederholungssteuerkonstrukt ist wieder eine eigene Funktion, die einen Pointer auf jede beliebige solche Funktion nimmt:

    void mehrfach_berechnen( berechnung_t berechnungPtr )
    {
        for( ; ; )
        {
            // Hier wird die übergebene Berechnung tatsächlich aufgerufen:
            berechnungPtr( );
    
            // Auswahlmenü
            cout << "1: Wiederholen" << endl;
            cout << "2: Ende" << endl;
            char auswahl;
            cin >> auswahl;
    
            if( '2' == auswahl )
                break;
        }
    }
    

    In main ist jetzt nur noch die Auswahl des Algorithmus':

    char auswahl = menue( );
    
    switch( auswahl )
    {
        case '2':
            // Übergibt die auszuführende Berechnung:
            mehrfach_berechnen( berechnen_einzelne_kraft );
    	break;
    
        case '3':
            mehrfach_berechnen( berechnung_lagerkraefte );
            break;
    }
    

    PS: Man kann natürlich den Aufruf von mehrfach_berechnen( ) nochmal aus dem switch-Block rausziehen und statt dessen in den cases einen lokalen Funktionspointer entsprechend füllen. Das sind dann die Feinheiten für später...



  • Hat alles super Funktioniert danke.

    Wie funktioniert das for( ; ; ) ?

    Den Rest suche ich mir so zusammen. Muß alles in einer Präsentation erklären.
    Nur über das for mit leeren Inhalt habe ich nichts gefunden.



  • Genauso wie while(true).
    Goto-Ersatzcode:

    :startlbl
    //Schleifeninhalt
    //break würde so aussehen: goto afterlbl
    goto startlbl  //jedesmal nach der Schleife wird zurückgesprungen, man kann nur mit break; die Schleife abbrechen
    :afterlbl
    


  • wxSkip schrieb:

    Goto-Ersatzcode:

    Bin nicht so ganz überzeut, ob goto jemals ein Ersatzcode für etwas anderes ist...

    for( ; ; ) ist eine übliche Notation, um eine Endlosschleife zu produzieren. Es sticht sofort ins Auge und man weiß, was gemeint ist.

    Im ersten Argument wird definiert, was vor Start der Schleife ausgeführt werden soll, im dritten Argument, was nach jedem Durchlauf geschehen soll. Hier haben wir: nichts.
    Entscheidend ist nur der mittlere Teil: wenn die Bedingung darin falsch ist, bricht die Schleife ab. Ein komplett leerer Test wird als wahr angesehen, daher funktionert eine Endloschleife. Wenn Dir das zu haarig ist, verwende eine der anderen üblichen Schreibweisen statt dem for :
    while( 1 == 1 )

    while( true )

    Übrigens ist es nicht ganz unumstritten, was ich vorgeschlagen habe. Es gibt auch die Ansicht, dass, wenn man eine Abbruchbedingung hat (und das haben wir: die entsprechende Taste), man auch eine Schleife mit Abbruchbedingung nehmen soll - und eine Endlosschleife nur dann, wenn sie wirklich endlos läuft.

    Man hätte z.B. das auch so schreiben können:

    char auswahl = '\0';
    do
    {
        ....
        cin >> auswahl;
    } while( '2' != auswahl );
    

    Das habe ich deshalb nicht gemacht, da die Variable, die über den Abbruch entscheidet ( auswahl ), außerhalb des Schleifenkörpers definiert werden muss. Wenn die Schleife länger wird, wird der Abstand (Anzahl der Zeilen) zwischen Definition und erstmaligem Gebrauch auch größer. Diese vertikale Distanz sollte immer so klein wie möglich bleiben.

    Letztlich ist es Geschmackssache, und ich denke, wenn auf den ersten Blick ersichtlich ist, was das Programm tut und auch unsere Abruchbedingung mit dem break gut zu sehen und eindeutig ist, man den pragmatischen Weg so gehen kann. Nur falls jemand bei Deiner Präsentation fragt...



  • minastaros schrieb:

    Übrigens ist es nicht ganz unumstritten, was ich vorgeschlagen habe. Es gibt auch die Ansicht, dass, wenn man eine Abbruchbedingung hat (und das haben wir: die entsprechende Taste), man auch eine Schleife mit Abbruchbedingung nehmen soll

    Generell ist es sicher vorzuziehen, auf eine der üblichen Schleifen ohne Sprünge aus dem Rumpf zurückzugreifen. Manchmal muss man so aber Code duplizieren oder Hilfsvariablen einführen, und dann spricht nichts gegen ein continue oder break im Rumpf.

    minastaros schrieb:

    und eine Endlosschleife nur dann, wenn sie wirklich endlos läuft.

    Ich kann mir da gerade nichts vorstellen, was kein Fehler oder unschön programmiert ist (wie ein Thread, der von aussen terminiert wird). Hast du ein Beispiel?



  • Nexus schrieb:

    Manchmal muss man so aber Code duplizieren oder Hilfsvariablen einführen, und dann spricht nichts gegen ein continue oder break im Rumpf.

    Genau, deswegen mache ich das ja auch und sehe auch keinen Grund, es nicht zu tun, solange es gleich erkennbar ist (im Gegensatz z.B. zu so "Tricks", die Abbruchbedingung von for-Schleifen zu manipulieren). Ich meine jedoch, in Büchern zum Thema "sauberes Programmieren" sowas gelesen zu haben, ähnlich wie "jede Schleife nur ein Ausstiegspunkt". Wobei die guten Bücher auch sagen, die Empfehlungen zwar zu bedenken, es aber pragmatisch angehen zu lassen.
    Ich wollte rellik nur darauf hinweisen, dass eine solche Frage kommen könnte.

    Nexus schrieb:

    Ich kann mir da gerade nichts vorstellen, was kein Fehler oder unschön programmiert ist (wie ein Thread, der von aussen terminiert wird). Hast du ein Beispiel?

    Ein embedded System, was so lange laufen soll, bis man tatsächlich die Spannung wegnimmt. Also etwas, wo die Schleife nicht durch "normale" Ereignisse wie hier durch eine Benutzereingabe beendet wird, sondern nur im äußersten Extremfall durch einen übergeordneten Fehler.

    Edit: ich würde auch in jenen Threads durch eine Endlosschleife deutlich zu machen, dass die Schleife immer laufen sollte. Spricht ja nichts gegen eine Abbruchbedingung.


Anmelden zum Antworten