array inhalt aus klasse in main übergeben
-
In der Datei.cpp gibt es eine Klasse, die den Inhalt von Dateien ausließt, was auch funktioniert. Wie lässt sich der inhalt von Buffer jetzt in der main.cpp abfragen? Hier die Dateien:
Datei.h:
/Datei_h #ifndef Datei_h #define Datei_h #include <iostream> #include <string> #include <fstream> #include <stdlib.h> using namespace std; //std:: fällt weg class Datei{ private: char *neubuffer; //Datenbuffer unsigned int minFilesize; unsigned int faktor; unsigned int neusize; ifstream quelle; //Datei-Handle string Dateiname; public: char *buffer; //Datenbuffer unsigned int size; Datei(); //constructor ~Datei(); //destructor void oeffnen(string Dateiname); int groesse(); void lesen(char *buffer, unsigned int &size); void schliessen(); void anpassen(unsigned int minFilesize, unsigned int &size, char *buffer); }; #endif //Datei_hDatei.cpp:
#include <iostream> #include <string> #include <fstream> #include <stdlib.h> #include "Datei.h" using namespace std; //std:: fällt weg //constructor Datei::Datei(){ size = 0; buffer = new char[size]; } //destructor Datei::~Datei(){ delete [] buffer; } //oeffnen void Datei::oeffnen(string Dateiname){ quelle.open(Dateiname.c_str(), ios::binary|ios::in|ios::ate); //Öffne Datei aus Parameter if (! quelle.good()){cout << "Error: Could not open file " << endl;}//Fehlermeldung wenn Datei } //size int Datei::groesse(){ size = quelle.tellg(); //Dateigrösse durch Endposition ermitteln return size; } //auslesen void Datei::lesen(char *buffer, unsigned int &size){ buffer = new char [size]; //allocate memory for file content quelle.seekg(0); //setze die Leseposition auf 0 gemessen vom Dateianfang quelle.read(buffer, size); //Datei auslesen } //schliessen void Datei::schliessen(){ quelle.close(); //Datei schließen } //Einheitsgroesse void Datei::anpassen(unsigned int minFilesize, unsigned int &size, char *buffer){ faktor = minFilesize/size; //Faktor zur Umrechnung in Einheitsgröße neusize = faktor*size; //neue Größe neubuffer = new char [neusize]; //allocate memory for file content for(unsigned int h=0; h<neusize; h+=(4*faktor)){ //Daten kopireren bis Einheitsgröße erreicht for(unsigned int i=0; i<4; i++){ for(unsigned int j=0; j<faktor; j++){ neubuffer[(i+j*4)+h]=buffer[i+h/faktor]; } } } buffer = neubuffer; size = neusize; delete[] neubuffer; //Speicher wieder frei geben }und die main.cpp:
#include <iostream> #include <string> //#include <pthread.h> //-lpthread linken //#include <SerialStream.h> //-lserial linken #include "Datei.h" using namespace std; //std:: fällt weg int main() { unsigned int size; char *buffer; int minFilesize = 50000; Datei neueDatei; neueDatei.oeffnen("../Figuren/default.hex"); size = neueDatei.groesse(); buffer = new char[size]; cout << "size " << size << endl; neueDatei.lesen(buffer, size); for(unsigned int i=0; i<size; i++){cout << buffer[i];} neueDatei.schliessen(); neueDatei.anpassen(minFilesize, size, buffer); delete [] buffer; return 0; }Bin dankbar für alle Tips. Die for-Schleife in main.cpp liefert nicht die gewünschten Daten.
-
Laserandi schrieb:
In der Datei.cpp gibt es eine Klasse, die den Inhalt von Dateien ausließt, was auch funktioniert. Wie lässt sich der inhalt von Buffer jetzt in der main.cpp abfragen? Hier die Dateien:
über einen getter
außerdem solltest du copyctor private machen, sonst geht dann 100pro was schief

um mal etwas ausführlich zu werden als heut nacht:
/*header*/ #ifndef H_TEST_INCLUDED #define H_TEST_INCLUDED //#include <iostream> //#include <fstream> //#include <cstdlib> //so heißt der header wirklich //brauchst du nicht #include <string> #include <vector> //unser dynamisches array //using namespace std; //das hier tut man in headern niemals - hebelt namespaces ja komplett aus(überall, wo es includiert wird) class Datei { private: // char *neubuffer; //Datenbuffer //so würde man das vll in C machen, in C++ gibts sichere und bequemere Wege :) std::vector <char> buffer; //ein array (beliebiger länge) of char //unsigned int minFilesize; //unsigned int faktor; //unsigned int neusize; //ifstream quelle; //Datei-Handle //nichts davon sollte ein member sein, außer: std::string dateiname; public: // char *buffer; //Datenbuffer // unsigned int size; // brauchst du hier nicht Datei(); //standardconstructor explicit Datei(const std::string &dateiname); //erspart dem nutzer einen fkt.-aufruf //große objekte(ab ca. 2DWORDs) übergibt man nicht mehr by value sondern by (const) reference //explicit, damit nicht irgendwo ein string ausversehen in eine Datei umgewandelt wird, kurzes bsp: //Datei get_savegame() //{ // return "asd"; //const char* -> string -> Datei //} //wenn das wer will, muss er schon // return Datei("asd"); //schreiben //es gibt aber durchaus fälle, wo so eine implizite umwandlung durchaus sinn macht //~Datei(); //destructor //brauchst du auch nicht mehr //void oeffnen(string Dateiname); void oeffnen(const std::string &dateiname); //int groesse(); //fkt, die nichts am objekt ändern, sollten dies auch kennzeichnen (const correctness): //int groesse() const; //nun ist der rückgabetyp aber signed(kann pos. und neg. werte annehmen - ne datei ist aber immer >= 0 ;o)), also: unsigned int groesse() const; //void lesen(char *buffer, unsigned int &size); //das hier können viel hübscher tun: //std::vector<char> data(); //auch diese fkt ändert das objekt wieder nicht, also: //std::vector<char> data() const; //nun können wir es dem nutzer aber auch noch überlassen, ob er überhaupt eine kopei erstellen will //oder ihm eine (konstante) referenz reicht - der vorteil sollte offensichtlich sein: wir müssen keinen //riesigen block an daten kopieren const std::vector<char>& data() const; void schliessen(); //lassen wir so, brauchen wir aber eigtl nicht mehr //(da wir den stream nicht die ganze zeit geöffnet lassen) //void anpassen(unsigned int minFilesize, unsigned int &size, char *buffer); //was die fkt hier machen soll, versteh ich nicht so ganz }; #endif //#ifndef H_TEST_INCLUDED/*source*/ #include <algorithm> //brauchen wir beim einlesen #include <iterator> //brauchen wir beim einlesen #include <fstream> #include <string> //haben wir zwar schon im header, aber schaden tuts auch nicht ^^ #include <vector> //haben wir zwar schon im header, aber schaden tuts auch nicht ^^ #include "Datei.h" using namespace std; //std:: fällt weg //ist hier ok - aber es gibt auch mehrere, die das std:: lieber immer mitschreiben //immerhin ist es eine extrem nützliche angabe, wo die fkt/das objekt herkommt Datei::Datei() { //hier müssen wir jetzt gar nichts mehr tun :clown: } Datei::Datei(const std::string &dateiname) : dateiname(dateiname)//initialisierungsliste // was hier passiert ist gerade ein wenig doof zu erkennen, weil ich beides gleich genannt habe, aber ist // nichts weiter als ein konstruktor-aufruf eines members (wenn hier nichts steht, wird der default- // konstruktor aufgerufen // später in der eigtl Fkt., können dann nur noch Zuweisungen ausgeführt werden { ifstream dateistream(dateiname.c_str()); //das einlesen ist evtl ein wenig schwer verständlich std::istream_iterator<char> anfang(dateistream); std::istream_iterator<char> ende; buffer.assign(anfang, ende); //iteratoren sind in C++ ein sehr mächtiges Konzept, es geht eigtl alles damit: //in der ersten (der drei zeilen) zeile wird ein iterator(vll mit zeiger übersetzbar) erstellt, der auf //den anfang der datei zeigt //danach wird ein ungültiger zeiger erstellt, den gleichen wert nimmt der erste zeiger auch an, //wenn dieser oft genug weitergeschoben wird //schließlich wird dem buffer alles zugewiesen (assign), was in dem intervall [anfang, ende) liegt //== einschließlich anfang, ende ist aber nicht mehr mit dabei(weil ende ja auch "leer" ist } void Datei::oeffnen(const std::string &dateiname) { this->dateiname = dateiname; //hier ist this nötig, weil der parameter den gleichen namen wie der member hat //und diesen somit überdeckt. wenn das nicht so wäre, bräuchte man das this auch nicht (und sollte es weglassen^^) //wir erinnern uns an das einlesen: ifstream dateistream(dateiname.c_str()); //hier ist es egal, welchen dateinamen wir nehmen, da beide gleich sind^^ std::istream_iterator<char> anfang(dateistream); std::istream_iterator<char> ende; buffer.assign(anfang, ende); } unsigned int Datei::groesse() const { return buffer.size(); //unser buffer weiß selbst, wie lang er ist } const std::vector<char>& Datei::data() const { return buffer; } void Datei::schliessen() { std::vector<char>().swap(buffer); //ist ein wenig tricky: //hier wird der buffer nicht nur geleert sondern auch noch sein speicherplatz freigegeben //da das nicht so einfach geht (clear() gibt den speicherbereich auch nicht wieder frei) //müssen wir eine andere (leere) instanz erzeugen (std::vector<char>()) und dann unseren //vollen vector mit dem anderen tauschen (swap(buffer)), man könnte auch etwas ausführlicher //so etwas schreiben: //std::vector <char> leerer_vector; //leerer_vector.swap(buffer); //bzw. buffer.swap(leerer_vector) - das ist egal, wie rum //nur bei unserer variante können wir die reihenfolge nicht ändern, weil in den klammern //eine referenz stehen muss, ein temporär erzeugtes objekt kann aber nicht an eine //referenz gebunden werden }(ich merk gerade, dass ich doch ein paar mal den namespace (unnötigerweise) mit angegeben habe (std::) - lass dich davon einfach nicht verwirren^^
also ohne die ganzen kommentare und (du erinnerst dich hoffentlich noch an das öffnen, was 2mal in unserem code vorkommt) den doppelten code würde das dann so aussehen:
/*header*/ #ifndef H_TEST_INCLUDED #define H_TEST_INCLUDED #include <string> #include <vector> class Datei { private: std::vector <char> buffer; std::string dateiname; void fill_buffer(); public: Datei(); explicit Datei(const std::string &dateiname); void oeffnen(const std::string &dateiname); void schliessen(); unsigned int groesse() const; const std::vector<char>& data() const; }; #endif //#ifndef H_TEST_INCLUDED/*source*/ #include <algorithm> #include <iterator> #include <fstream> #include <string> #include <vector> #include "Datei.h" Datei::Datei() {} Datei::Datei(const std::string &dateiname) : dateiname(dateiname) { fill_buffer(); } void Datei::fill_buffer() { std::ifstream dateistream(dateiname.c_str()); std::istream_iterator<char> anfang(dateistream); std::istream_iterator<char> ende; buffer.assign(anfang, ende); } void Datei::oeffnen(const std::string &dateiname) { this->dateiname = dateiname; fill_buffer(); } unsigned int Datei::groesse() const { return buffer.size(); } const std::vector<char>& Datei::data() const { return buffer; } void Datei::schliessen() { std::vector<char>().swap(buffer); }wie du jetzt (wahrscheinlich eher nicht^^) siehst, müssen wir den kopier-konstruktor und zuweisungsoperator auch nicht mehr private machen.
das hätten wir zuvor gemacht, weil die vom compiler generierten keine tiefe kopie sondern nur eine flache machen(nur der pointer wird kopiert, aber nicht das, worauf er zeigt). also könnte z.bsp. ein und derselbe speicher zweimal nacheinander freigegeben werden - und das gibt beim 2. mal natürlich nen großes problem: der speicher gehört deinem programm gar nicht mehr -> das betriebssystem wird dein programm einfach sofort schließen.
das ganze umgeht man, indem man kopierctor und op= entweder richtig implementiert oder einfach private macht(und dann nur deklariert, aber nicht definiert)wenn du noch fragen hast, dann frag ruhig^^
und sag mal noch, was die komische fkt machen soll, die nen faktor und was weiß ich nicht noch alles übergeben bekommt
bb
edit: im source fehlte noch 3x "Datei::", sry^^
-
WOW!
Viiiiieeeeelen Dank, unskilled.
Da hast Du Dir aber wirklich viel Mühe gegeben.
Jetzt brauch ich erstmal ein paar Tage um alles
zu verdauen (habe vor ca. 7 Jahren im Studium
das letzte mal mit c/c++ gearbeitet).Die fkt unter "//Einheitsgroesse" hat die
Aufgabe, die aus der Datei ausgelesenen Daten
zu vervielfachen, bis die vorher festgelegte
Einheitsgröße erreicht wird.
Bsp. Inhalt der Quelldatei:
"abcdefgh..."
wird nach der Funktion:
"abcdabcdabcdabcdefghefghefghefgh..."Die Anzahl der Kopiervorgänge ist der Faktor.
Wahrscheinlich ist meine Lösung nicht die
einfachste aber sie funktioniert.Jetzt werde ich mir erst mal Deinen Quellcode
anschauen und mich dann wieder melden.
-
dann hier noch mal der komplette source-code:
#include <iterator> #include <iomanip> #include <fstream> #include <string> #include <vector> #include "test.h" Datei::Datei() {} Datei::Datei(const std::string &dateiname) : dateiname(dateiname) { fill_buffer(); } void Datei::fill_buffer() { std::fstream dateistream(dateiname.c_str()); dateistream << std::noskipws; std::istream_iterator<char> anfang(dateistream); std::istream_iterator<char> ende; buffer.assign(anfang, ende); } void Datei::oeffnen(const std::string &dateiname) { this->dateiname = dateiname; fill_buffer(); } unsigned int Datei::groesse() const { return buffer.size(); } const std::vector<char>& Datei::data() const { return buffer; } void Datei::schliessen() { std::vector<char>().swap(buffer); } void Datei::vervielfache_inhalt(std::size_t mindest_groesse) { buffer.reserve(mindest_groesse); std::size_t l = groesse(); if(l == 0) throw std::runtime_error("0*x < mindest_groesse"); std::ofstream stream(dateiname.c_str(), std::ios_base::app); while(mindest_groesse > groesse()) { for(std::vector<char>::size_type i(0); i != l; ++i) buffer.push_back(buffer[i]); stream.write(&buffer[0], l); } }im header fehlt noch die zeile:
void vervielfache_inhalt(std::size_t mindest_groesse);anwendung:
#include <iostream> #include "test.h" int main() { Datei tmp("Q:/a.txt"); tmp.vervielfache_inhalt(10); std::cout.write(&*tmp.data().begin(), tmp.groesse()); }geht mit sicherheit auch noch toller, aber mir ist gerade nichts besseres eingefallen....
bb : >
-
Wenn ich unter Linux compiliere, bekomme ich folgende Fehlermeldungen:
1. Datei.cpp:53: Fehler: »runtime_error« ist kein Element von »std«
2. Datei.cpp:62: Fehler: »stream« wurde in diesem Gültigkeitsbereich nicht definiertGenerelle Frage: Warum benutzt Du vectoren. in vielen Tutorials und Büchern liesst man, dass dynamischer Speicher mit new und delete (auch in c++) verwendet wird?
-
Laserandi schrieb:
Generelle Frage: Warum benutzt Du vectoren. in vielen Tutorials und Büchern liesst man, dass dynamischer Speicher mit new und delete (auch in c++) verwendet wird?
hmm... ich nutze so gut wie nie new/delete
sollte man auch wo möglich umgehen (stichwort RAII)runtime_error ->
#include <stdexcept>2. Datei.cpp:62: Fehler: »stream« wurde in diesem Gültigkeitsbereich nicht definiert
versteh ich nicht
hast du dich auch ganz sicher nicht vertippt?buffer.reserve(mindest_groesse);
das hier müsstest du mal noch korrigieren:
reserve(mindest_groesse/groesse() + mindest_groesse%groesse() == 0 ? 0 : groesse);std::size_t l = groesse();
würd ich btw length oder size oder irgendwie so nennen - l ist genau wie I so nen no-go-bezeichner
bb
-
Der runtime_error ist weg. Dafür bekomme ich jetzt bei:
buffer.reserve(mindest_groesse/groesse() + mindest_groesse%groesse() == 0 ? 0 : groesse);Datei.cpp:5o: Fehler: invalid use of member (did you forget the »&« ?)
zu 2.) Alles richtig geschrieben bzw. copy-paste gemacht.
-
da (fast) ganz rechts fehlen noch 2 klammern - eine auf und eine zu ;o)
-
Jo, da hätte ich auch mal alleine darauf kommen können. Jetzt kompiliert es.
DANKE.