Vektoren in csv Datei abspeichern in öffnen...
-
Moin,
ich bin gerade am verzweifeln. Ich habe in meinem Studium eine Projektaufgabe in der ich einen Funktionsgenerator programmieren soll. Dieser soll Sinus- und Kosinusverläufe darstellen. Der Benutzer gibt Die Parameter selber ein. Das ganze mache ich mit Klassen nur habe ich jetzt das Problem, dass die zuvor ausgerechneten Vektoren, die ich in einen protected Bereich einer der Klassen ablege, zufällige Werte zugewiesen bekommen. Ich habe die Vektoren an einer Stelle nicht richtig deklariert nur kann ich sie nicht finden. Am Ende werden die Vektoren, wenn ich sie in der Klasse die erbt oder in der Klasse die vererbt, auf die Werte 0 setzte, in der csv Datei mit genau diesen Werten ausgegeben was dann 0 ist.
Ich werde im folgenden nur die wichtigen Auschnitte meines Programms einfügen. Es ist trotzdem sehr viel, tut mir Leid aber ich sehe keine andere Möglichkeit. ChatGPT haut auch nur noch Blödsinn raus. Ich wäre euch sehr dankbar wenn ihr sucht mir zu helfen. Danke.main-Funktion:
cout << "Sie haben die Sinusfunktion gew\204hlt\n"; _getch(); sinus *Objekt_1 = new sinus(); Objekt_1 -> umwandlung_str_double(); Objekt_1 -> berechnung(); Objekt_1 -> ausgabe_parameter(); datei *Objekt_5 = new datei(); Objekt_5 -> erstellung(); delete Objekt_1;
sinus.h
Sinus.h #ifndef SINUS.H #define SINUS.H #include "parameter.h" #include <iostream> #include <vector> using namespace std; class sinus : public parameter { public: sinus(); virtual ~sinus(); virtual berechnung(); virtual void ausgabe_parameter(); protected: vector <double> sinus_; vector <double> zeit_; }; #endif // SINUS datei.h:
sinus.cpp: Das selbe habe ich mit der Cosinus.cpp gemacht. Außer das im Konstruktor der Cosinus Vektor deklariert wird
#include "sinus.h" #include <vector> #include <math.h> #include <conio.h> using namespace std; sinus :: sinus() { sinus_ = {0}; zeit_ = {0}; } sinus :: berechnung() { for ( i = 0 ; i < anzahl_funktionswerte ; ++i) { zeit_[i] = i * abtastrate; sinus_[i] = amplitude * sin(2 * M_PI * frequenz * zeit_[i]); cout << "zeit[" << i << "] = " << zeit_[i] << ", sinus[" << i << "] = " << sinus_[i] << endl; } getch(); }
datei.h
#ifndef DATEI.H #define DATEI.H #include "cosinus.h" using namespace std; class datei : public cosinus { public: datei(); virtual ~datei(); virtual erstellung(); }; #endif // DATEI
cosinus.cpp
#ifndef COSINUS.H #define COSINUS.H #include <iostream> #include <vector> #include "sinus.h" using namespace std; class cosinus : public sinus { public: cosinus(); virtual ~cosinus(); virtual berechnung(); virtual void ausgabe_parameter(); protected: vector <double> cosinus_; int i; }; #endif // COSINUS datei.cpp: #include <iostream> #include <conio.h> #include <vector> #include <cstdlib> // Für system() #include <math.h> using namespace std; datei :: datei() { }
datei.cpp
datei :: erstellung() { cout << "zeit[" << i << "] = " << zeit_[i] << ", sinus[" << i << "] = " << sinus_[i] << endl; string name;//Dateinamenertsellung do { _getch(); system("cls"); cout << "Dateinamen : "; cin >> name; if (name.size()>4 || name.substr(name.size() - 4) == ".csv") { cout << "Der Dateiname muss auf .csv enden\n"; } }while (false); FILE *f; // Zeiger auf f auf die FILE_Struktur f = fopen(name.c_str(),"w"); // Öffnen der Datei mit dem Namen "name", "w" steht für write for (size_t i = 0; i < zeit_.size(); ++i) { fprintf(f, "%.2f, %.2f, %.2f\n", zeit_[i], sinus_[i]); } fclose(f); cout << "Datei wurde erfolgreich erstellt: " << name << endl; _getch();
-
Bitte packe deinen Code in Code-Tags (Button "</>"). Mittels des "3-Punkte" Menüs kannst du deinen Beitrag "Bearbeiten".
So wie ich deinen Code verstehe, macht die Ableitung hier keinen Sinn! Auch wenn du
datei
voncosinus
abgeleitet hast, welches wiederum vonsinus
abgeleitet ist, hat das Objektdatei *Objekt_5 = new datei();
nicht die Daten des vorher erzeugten Objektssinus *Objekt_1 = new sinus();
-> daher ist die Ausgabe nur eine0
(weil du es so im Konstruktor vonsinus
angegeben hast:zeit_ = {0};
).Du solltest generell eine Trennung von Ein-/Ausgabe (IO) und Berechnungen (Logik) in deinem Code haben.
Und auch nicht C++ mit C-Funktionen (wie_getch()
,fopen(...)
,fprintf(...)
mischen).Und niemals
using namespace std;
in Header-Dateien (dadurch wird das gesamte Namensbereich-System konterkariert)!!!Mit welchem Buch oder Tutorial lernst du denn C++?
-
@Th69 Wir haben Übungen die wir zuvor bearbeitet haben sollten, nur ist deren Umfang nicht mit dem der Projektaufgabe zu vergleichen. Ich habe das so verstanden, das die Klasse datei auf den protected bereich der Klassen Sinus und Cosinus zugreifen kann. Ich hatte schon bei der Berechnung das Problem da Sinus und Cosinus von Parameter erben. Da hatte ich den Dozenten gefragt und als Lösung des Problems habe ich die Parameter der Funktion in dem Konstruktor der Parameter Klasse auf Null gesetzt. Wenn ich nun deine Antwort richtig verstehe, muss ich als erstes ein Objekt der Datei Klasse öffnen wenn diese von Cosinus und Sinus erbt. Dann könnte die Datei Klasse auf die Werte der Vektoren zugreifen. Das habe ich ebenfalls ausprobiert nur dann funktioniert die Berechnung der Vektoren nicht mehr.
-
Du mußt entweder die Daten von dem einen Objekt in das andere Objekt kopieren oder aber du verwendest nur genau ein Objekt (für welches sowohl die Berechnung als auch die Dateiausgabe ausgeführt wird).
Dein Beitrag deutet darauf hin, daß du nicht den Unterschied zwischen einer Klasse und einem Objekt, d.h. die Basis von OOP, richtig verstehst. Eine Klasse ist nur eine Vorlage und erst durch die Erstellung eines Objekts (zur Laufzeit) werden die Daten geändert.
Wenn du zwei verschiedene Objekte hast, so sind deren Daten komplett voneinander getrennt (egal wie die Vererbungshierarchie aussieht).Edit:
Noch etwas: C++ ist nicht Java oder C#. Für lokale Objekte benötigt man keine dynamische Speicherallokation mittelsnew
.
Es reichtsinus Objekt_1;
So vergißt man auch kein
delete
(wie bei dir fürObjekt_5
).
Btw: Du solltest bessere Variablennamen benutzen...PS: Danke für das Hinzufügen der Code-Tags - nun ist dein Code besser zu lesen!
-
@Th69 Ich finde es auch gruselig das ich das immer noch nicht verstehe. Ich habe es jetzt so programmiert:
datei *Objekt_1 = new datei(); Objekt_1 -> umwandlung_str_double();/ Objekt_1 -> berechnung(); Objekt_1 -> ausgabe_parameter(); Objekt_1 -> erstellung();
Das funktioniert auch, bis auf die erstellung(). Ich habe noch nicht verstanden wo ich die Vektoren deklarieren muss wenn. Ich verstehe nur nicht wie die Klassen auf die Werte der abgeleiteten Klassen zugreifen und wann ich etwas deklarieren muss.
Die Variablen Namen werde ich ändern sobald das das kleinste Problem ist.
-
Genau so meinte ich es und es sollte so funktionieren.
Jetzt wo ich deinen Code noch mal detaillierter angesehen habe: compiliert er fehlerfrei?
Denn z.B.virtual berechnung();
innerhalb der Klassendefinition sollte einen Fehler ausgeben, da der Datentyp (z.B.void
) fehlt.Welchen Compiler und/oder welche IDE verwendest du?
Bei einer IDE solltest du auch recht einfach den Debugger benutzen können, um den Code zeilenweise zu verfolgen (und die Variablenwerte zu sehen).
Ansonsten lass dir die Variablenwerte, z.B.zeit_.size()
, mal perstd::cout
ausgeben.
-
@Th69 Er kompilierte jedes Mal fehlerfrei . In der CSV Datei standen jedoch die Werte mit denen ich die Vektoren deklariert habe, nämlich 0. Ich benutze CodeBlocks
-
Ich spezifiziere das Problem einmal:
die main-Funktion habe ich so beibehalten, da die Berechnung funktioniert.datei.cpp:
#include "datei.h" #include "cosinus.h" #include <iostream> #include <conio.h> #include <vector> #include <cstdlib> // Für system() #include <math.h> using namespace std; datei :: datei() { sinus_ = {0}; zeit_ = {0}; cosinus_ = {0}; } datei :: erstellung() { //cout << "zeit[" << i << "] = " << zeit_[i] << ", sinus[" << i << "] = " << sinus_[i] << endl; string name;//Dateinamenertsellung do { _getch(); system("cls"); cout << "Dateinamen : "; cin >> name; if (name.size()>4 || name.substr(name.size() - 4) == ".csv") { cout << "Der Dateiname muss auf .csv enden\n"; } }while (false); FILE *f; // Zeiger auf f auf die FILE_Struktur f = fopen(name.c_str(),"w"); // Öffnen der Datei mit dem Namen "name", "w" steht für write for (size_t i = 0; i < zeit_.size(); ++i) { fprintf(f, "%.2f, %.2f, %.2f\n", zeit_[i], sinus_[i]); } fclose(f); cout << "Datei wurde erfolgreich erstellt: " << name << endl; _getch(); }
Wenn ich hin dieser datei.cpp die Vektoren nicht deklariere bekomme ich eine Fehlermeldung. Jedoch stehen diese Werte in der CSV.
cosinus.h:
#ifndef COSINUS.H #define COSINUS.H #include <iostream> #include <vector> #include "sinus.h" using namespace std; class cosinus : public sinus { public: cosinus(); virtual ~cosinus(); virtual void berechnung(); virtual void ausgabe_parameter(); protected: vector <double> cosinus_; }; #endif // COSINUS
im protected Bereich steht der Vektor damit datei.cpp darauf zugreifen kann.
sinus.h:
#ifndef SINUS.H #define SINUS.H #include "parameter.h" #include <iostream> #include <vector> using namespace std; class sinus : public parameter { public: sinus(); virtual ~sinus(); virtual void berechnung(); virtual void ausgabe_parameter(); protected: vector <double> sinus_; vector <double> zeit_; }; #endif // SINUS
Das gleiche habe ich bei der sinus.h gemacht.
parameter.h:
#ifndef PARAMETER.H #define PARAMETER.H #include <iostream> #include <vector> using namespace std; class parameter { public: parameter(); virtual ~parameter(); void umwandlung_str_double (); virtual void ausgabe_parameter(); protected: double amplitude; double frequenz; double abtastrate; double anzahl_funktionswerte; }; #endif
cosinus.cpp
#include "cosinus.h" #include <vector> #include <math.h> #include <conio.h> using namespace std; cosinus :: cosinus() { } void cosinus :: berechnung() { vector <double> cosinus_(anzahl_funktionswerte); vector <double> zeit_(anzahl_funktionswerte); int i; for ( i = 0 ; i < anzahl_funktionswerte ; ++i) { zeit_[i] = i * abtastrate; cosinus_[i] = amplitude * cos(2 * M_PI * frequenz * zeit_[i]); cout << "zeit[" << i << "] = " << zeit_[i] << ", cosinus[" << i << "] = " << cosinus_[i] << endl; } getch(); } void cosinus :: ausgabe_parameter() { cout << "Amplitude : " << amplitude << "\n" << "Frequenz : " << frequenz << "\n" << "Abtastrate : " << abtastrate << "\n" << "Anzahl der Funktionswerte : " << anzahl_funktionswerte << "\n"; } cosinus :: ~cosinus() { }
sinus.cpp:
#include "sinus.h" #include <vector> #include <math.h> #include <conio.h> using namespace std; sinus :: sinus() { } void sinus :: berechnung() { vector <double> sinus_(anzahl_funktionswerte); vector <double> zeit_(anzahl_funktionswerte); int i; for ( i = 0 ; i < anzahl_funktionswerte ; ++i) { zeit_[i] = i * abtastrate; sinus_[i] = amplitude * sin(2 * M_PI * frequenz * zeit_[i]); cout << "zeit[" << i << "] = " << zeit_[i] << ", sinus[" << i << "] = " << sinus_[i] << endl; } getch(); } void sinus :: ausgabe_parameter() { cout << "Amplitude : " << amplitude << "\n" << "Frequenz : " << frequenz << "\n" << "Abtastrate : " << abtastrate << "\n" << "Anzahl der Funktionswerte : " << anzahl_funktionswerte << "\n"; } sinus :: ~sinus() { }
parameter.cpp:
#include "parameter.h" #include <iostream> #include <conio.h> #include <vector> #include <math.h> using namespace std; //double amplitude , frequenz , abtastrate , anzahl_funktionswerte; //string amplitude_str, frequenz_str , abtastrate_str , anzahl_funktionswerte_str; // Implementierung der Funktionen/Methoden parameter :: parameter() { amplitude = 0; frequenz = 0; abtastrate = 0; anzahl_funktionswerte = 0; } double ueberpruefung_umwandlung ( string wert_str) { if(wert_str.find_first_not_of("1234567890.")!=string::npos) { cout << "Falsche Eingabe\n"; getch(); return 0; } double wert_double = stod (wert_str); return wert_double; } void parameter :: umwandlung_str_double()//Umwandlung und Überprüfung des Strings, amplitude wird sofort in en protected bereich geschoben und ist für abgeleitete klassen verfügbar { string amplitude_str, frequenz_str, abtastrate_str, anzahl_funktionswerte_str; do { system("cls"); cout << " Amplitude : "; cin >> amplitude_str; amplitude = ueberpruefung_umwandlung(amplitude_str); }while (amplitude == 0 ); do { system("cls"); cout << "Frequenz : "; cin >> frequenz_str; frequenz = ueberpruefung_umwandlung(frequenz_str); }while (frequenz == 0); do { system("cls"); cout << "Abtastrate : "; cin >> abtastrate_str; abtastrate = ueberpruefung_umwandlung(abtastrate_str); }while (abtastrate == 0); do { system("cls"); cout << "Anzahl Funktionswerte : "; cin >> anzahl_funktionswerte_str; anzahl_funktionswerte = ueberpruefung_umwandlung(anzahl_funktionswerte_str); }while (anzahl_funktionswerte == 0); // Die gesetzen Parameter noch einmal gesammelt ausgeben und vielleicht die Möglichkeit anbieten die Werte (gezielt) zu ändern } parameter :: ~parameter() { }
Ich möchte gerne verstehen warum in dieser Datei (parameter.cpp) im Konstructor amplitude etc. auf 0 gesetzt werden muss. Ich weiß das dieses Problem so ähnlich ist wie das bei der Übertragung der Vektoren in die datei.cpp.
Edit: Wenn ich die Vektoren nicht in der datei.cpp im Konstruktor deklariere ist die csv-Datei leer (0kB) . Ich glaube ich habe verstanden warum man die Vektoren in sinus.cpp und cosinus.cpp mit 0 initialisieren muss. Da ich am Anfang ein Objekt der Klasse datei erstelle muss ich wenn diese Klasse auf die Methode erstellung() zugreift die Vektoren in sinus.cpp und cosinus.cpp initialisieren weil damit die Vektoren im protected Bereich initialisiert werden. durch die vorherige Ausführung der Berechnung werden die neuen Werte für die Vektoren überschrieben und in der Methode erstellung() wird auf diese zugegriffen werden. Das habe ich auch so programmiert nur es sind immer noch 0kB in der CSV. So langsam habe ich den Verdacht das das an etwas anderem liegt was ich gar nicht auf dem Schirm haben.
-
Ich hab jetzt nicht den ganzen Verlauf gelesen aber ich würde mal ganz strikt die Frage stellen, warum
sinus
undcosinus
eine "ist ein"-Beziehung haben. Das macht es auf keinen Fall einfacher oder gar übersichtlicher. Die ganzen leeren Destruktoren tragen zur Übersichtlichkeit auch kein Stück bei. Auf den ersten Blick würde ich behaupten, dass du ebenfalls alle Konstruktoren entfernen könntest, wenn du die Member direkt in der Klasse initialisieren würdest => exemplarisch fürparameter
:class parameter { public: void umwandlung_str_double (); virtual void ausgabe_parameter(); protected: double amplitude{0.}; // entweder explizit mit 0 double frequenz{}; // oder einfach so double abtastrate{}; double anzahl_funktionswerte{}; };
Ansonsten: Schreibe niemals - ja, wirklich niemals (es gibt in c++ wirklich nicht oft Empfehlungen die sich pauschalisieren lassen) -
using namespace std;
in den globalen scope eines Headers!Die Klasse
Datei
wirkt auf mich auch recht unnötig =>std::ofstream
Wenn du das ganze mal ein wenig reduziert hast (die Menge an Code ist kein Qualitätsmerkmal) und ggf. mal wirklich halbwegs modernes c++ nutzt (c++-11 oder aufwärts), dann schaue ich mir auch gerne mal dein eigentliches Problem an.
-
@DNKpp Ich bin Anfänger und ich studiere kein Informatik falls das jemand denkt ich habe keine Ahnung was eine "ist-ein" Beziehnung ist und was das mit der ünnötigen datei.h zutun haben soll. ich wollte lediglich wissen was falsch gelaufen ist bei der Übertragung der Vektoren in den protected Bereich.
Ich glaube ich belasse es dabei und frage meinen Dozenten in der nächsten Woche.
Danke für die Antworten .
-
@bittekeinbit sagte in Vektoren in csv Datei abspeichern in öffnen...:
ich habe keine Ahnung was eine "ist-ein" Beziehung ist
s. Vererbung (Programmierung):
(4. Absatz) Abgeleitete Klasse und Basisklasse stehen typischerweise in einer „ist-ein“-Beziehung zueinander.
Sieh dir das Beispiel mit der Basisklasse
Fahrzeug
an:- ein
Kraftfahrzeug
ist einFahrzeug
- ein
PKW
ist einKraftfahrzeug
- usw.
Bei deiner Vererbungshierarchie hieße es:
- ein
sinus
ist einparameter
- ein
cosinus
ist einsinus
- eine
datei
ist eincosinus
Und das ist nun mal falsch!
Was man machen könnte, wäre eine (abstrakte) Basisklasse
Function
zu erstellen, undSinus
sowieCosinus
wären jeweils davon abgeleitet.
Und die Dateiausgabe wäre eine (virtuelle) Funktion der Basisklasse.Üblicherweise verwendet man jedoch in C++ einfach die Überladung des
ostream& operator<<(ostream& out, MyClass& my)
, um die Ausgabe mittelsstd::cout
oderstd::ofstream
(in eine Datei) durchzuführen, s. das Beispiel in Überladen von Operatoren.
- ein
-
Guten Morgen @bittekeinbit,
ich würde Deinem Dozenten die Frage stellen, wie er auf die Idee kommt, Dir mit so wenig Vorkenntnissen in C++ eine solche Aufgabe vorzulegen.
Da scheint wohl so etwas wie ein Missverständnis vorzuliegen.
War denn C++ vorgegeben? Oder wurde eine C++ Vorlesung vorausgesetzt?
Mal schnell eine mathematische Aufgabe mit C++ ohne Vorkenntnisse in Bezug auf "Klasse" und "Objekte" zu bewältigen, halte ich für sinnlos.
Kannst Du nicht sowas wie Python nutzen?
-
Mich würde in diesem Fall die genaue Aufgabenstellung interessieren.
Wenn es "nur" darum geht, Funktionswerte in einer bestimmten Range zu erstellen, dann erscheint mir die Modellierung von Sinus und Cosinus als Klassen als zu viel.
Man könnte einen Rückgabetypen für Wertepaare (x,y) definieren und einen Overload des<<
Operators für Ausgabe auf dem Bildschirm und in eine Datei.Ansonsten: In eine Datei schreiben macht man in C++ mit
ofstream
nicht mitFILE
, was aus C stammt (https://en.cppreference.com/w/cpp/io/basic_ofstream) das klingt für C++ Neulinge erstmal etwas überraschend, aber Streams sind eine Abstraktion von etwas an das man Daten schreiben kann oder von was man Daten lesen kann. Daher kann man damit z.B.direkt Schreiben in eine Datei und schreiben auf die Bildschirmausgabe erschlagen.@bittekeinbit sagte in Vektoren in csv Datei abspeichern in öffnen...:
Ich möchte gerne verstehen warum in dieser Datei (parameter.cpp) im Konstructor amplitude etc. auf 0 gesetzt werden muss. Ich weiß das dieses Problem so ähnlich ist wie das bei der Übertragung der Vektoren in die datei.cpp.
Das ist so eine Eigenheit von C++ (und auch C). Wenn du irgendwo eine Variable wie z.B.
double
hast, dann gibt es irgendwo einen Speicherbereich für die Daten, aber der wird nicht auf einen bestimmten Wert gesetzt, sondern da steht halt, was an dem Speicherbereich so steht.
In der Theorie kann das vlt ein minimaler Performancevorteil sein, in der Realität ist das vor allem eine Fehlerquelle.
-
KISS: Keep It Simple and Smart.
Was spricht gegen folgende Strukturierung?
#include <iostream> #include <vector> /* ------------------------------- Typedefs und Co. ------------------------------- */ enum class Func2Calc { Sin, Cos, }; typedef struct { // ToDo: Fill me Func2Calc mFunc; ///< Gibt die math. Funktion an, welche wir berechnen wollen std::string mCSVOutName; ///< Dateiname der CSV Datei } tInputParameters; /* ------------------------------- Funktionen ------------------------------- */ bool ReadInputParameters(tInputParameters& P) { // ToDo: Fill me return true; } void CalculateXValues(std::vector<double>& XValues, const tInputParameters& P) { // ToDo: Fill me } void CalculateYValues(std::vector<double>& YValues, const tInputParameters& P, const std::vector<double>& XValues) { // ToDo: Fill me } bool OutputData(const std::string& FileName, std::vector<double>& XValues, std::vector<double>& YValues) { // ToDo: Fill me return true; } int main() { tInputParameters Params; std::vector<double> XValues; std::vector<double> YValues; // Lese Berechnungsparameter ein if (!ReadInoutParameters(Params)) { std::cout << "Erläuterung" << std::endl; return 0; } // Berechne X Werte CalculateXValues(XValues, Params); // Berechne Y Werte CalculateYValues(YValues, Params, XValues); // Gebe Daten aus if (!OutputData(Params.mCSVOutName, XValues, YValues)) { std::cout << "Erläuterung" << std::endl; return 0; } return 0; }
-
Nochmals Danke für die ganzen Antworten. Ich habe es jetzt nach mehrmaligen Umändern hinbekommen. Ich hatte noch nicht ganz die Vererbungshierarchie verstanden.