Einen Vector in mehreren Dateien abspeichern, ab einer bestimmten Dateigröße.



  • Sehr geehrte Foren-Mitglieder,

    ich bin noch sehr neu in der C++ Programmierung, jedoch ein paar grundsteine denke bekomme ich hin. Nun stoße ich auf das Problem das ich Daten in einen Vector (results) einlese und diesen zwischenspeichern möchte, allein um den RAM nicht mit Daten vollzumüllen. Dazu habe ich mir folgende abfrage geschrieben:

    if(results.size() > 100000)
    {
       static int count = 1;
       string part = "part";
       part += to_string(count);
       writeFile(part, results);
       results.clear();
       results.shrink_to_fit();
       count++;
    }
    

    Ich habe hier jedoch das problem, dass ich dadurch immer unterschiedlich große Dateien erhalte. Ich möchte jedoch immer ab bspw. 100 MB dateien abspeichern und den RAM leeren.
    Könnt ihr mir eine Idee aufzeigen wie ich das bewerkstelligen könnte?

    Grüße und vielen Dank im voraus! 🙂



  • Ich habe hier jedoch das problem, dass ich dadurch immer unterschiedlich große Dateien erhalte.

    Warum ist das ein Problem? Dein vector speichert ja Objekte einer bestimmten Klasse. Wenn es z.B. Strings sind, können die natürlich unterschiedlich lang sein. Du könntest eine Funktion size_t stored_size(const T &x) oder so ähnlich erstellen, bei der du dann für eine Typ ermitteln könntest, wie viel das entsprechende Objekt gespeichert so braucht. Dann berechnest du für ein Sample deines Vectors die Größe und rechnest das auf die Gesamtanzahl hoch. Das stored_size musst du dann für die jeweils verwendeten Typen implementieren.

    Ansonsten: brauchst du wirklich so viele Daten, dass der RAM ein Problem wird? Was, wenn du danach wieder auf ein bereits geschriebenes vector-Element zugreifen willst? Dann muss dein Ersatzvector erstmal wieder die Daten einlesen.

    Und letztendlich: ist shrink_to_fit wirklich nötig? Der vector wird doch wohl in deinem Fall wieder wachsen und man könnte den RAM doch einfach wiederverwenden.



  • Der Vector speichert ein struct und RAM wird eigentlich kein direktes Problem, jedoch erhalte ich bei einer vector Größe (results.size()) von ca 460000 ein bad_alloc und mein Programm bricht ab, bzw speichert im Vector nichts mehr. Der Prozesspeicher von Visual-Studio geht auch dort dann nicht höher als 2GB. So kam ich auf die Idee den vector einfach zu leeren und wegzuspeichern sobald eine gewisse Größe erreicht worden ist. Diese Prüfung und das wegspeichern erfolgt während des einlesen und abspeichern der Daten im Vector und da ich diesen nach dem wegspeichern leere, erzeuge ich an der stelle wo shrink_to_fit() steht eine Verkleinerung des Vectors auf eine capacity von 0.

    Als Hintergrund:
    Das Programm soll Daten einlesen (beliebig große txt-Dateien) und diese in ein csv-Format packen und abspeichern. Dazu wander ich über alle Dateien in einem Verzeichnis und speicher mir den Inhalt in ein struct ab, welches ich dann in einen Vector lege und mit dem einlesen der Daten fortfahre. Nachdem einlesen aller Daten habe ich vor die Daten zu sortieren und im csv-Format wieder abzuspeichern.





  • Danke, der Hinweis hat geholfen ein Switchen auf 64 Bit hat mein Problem gelöst.

    Allgemeine Frage, ich lese bspw. ca 350 MB Textdateien ein und erhalte beim abspeichern der Werte im RAM ca 3.5 GB an Zwischenspeicher. Die csv-Datei am ende ist jedoch nur 83MB groß. Meine Frage ist nun ob dieser Größen unterscheid bei der Arbeit mit Textdateien normal ist? Auch geht dabei gut Zeit ins land ...

    Eine einzulesende txt ist ungefähr so aufgebaut:

    line 1         $Anfang: Wert Wert
    line 2         blubblub: Wert
    line 3-x       ...
    line x         XYZ/ABC: wert   <- Indikator Klasse 1/2
    line x         ... 
    line 12-14     $Ende: Wert 
    line 15
    line 16        \f
    line 17        $Anfang: Wert 
    line x         ...
    

    Momentan lese ich das so ein:

    
    ....
    // global außerhalb der main()
    struct sammlung
    {
        string s, d, s, a, i, tag;
       Klasse1 klasse1;
       Klasse2 klasse2;
    
    };
    ....
    void readFile(const string& dateiname, struct sammlung& t, vector<sammlung>& results)
    {
    // Zwischenspeichern des aktuellen Structs
    	sammlung temp = t;
    	bool finished = false;
    	try
    	{
    		ifstream datei(dateiname);
    		while (datei.good())
    		{
                        // Zeilenweise einlesen und Spliten der Daten am trennenden Leerzeichen
    			string line;
    			getline(datei, line);
    
    			int pos = line.find_first_of(' ');
    			if (pos > 0)
    			{
    				string line1 = line.substr(0, pos);
    				string line2 = line.substr(pos);
    				line2 = ltrim(line2);
    				checkline(line1, line2, t);
    				
                                  // Check ob block zuende ist
                                    if (t.blockzuende != "")
    					finished = true;
    			}
    			else if(pos < 0 && finished == true)
    			{
    				string temp_z;
    				splitString(t.d, temp_z);
                                   
                                    // Füllen der inhalte vom struct nach Klasse 1
    				if (t.tag == "Klasse1_tag")
    				{
    					t.klasse1.setD(t.d);
    					t.klasse1.setZ(temp_z);
    					t.klasse1.setS(t.s);
    					t.klasse1.setU(t.u);
    					t.d = t.s = t.u = "";
    				}
                                   
                                    // Füllen der inhalte vom struct nach Klasse 2
    				if (t.tag == "Klasse2_tag")
    				{
    					t.klasse2.setD(t.d);
    					t.klasse2.setZ(temp_z);
    					t.klasse2.setS(t.s);
    					t.klasse2.setU(t.u);
    					t.d = t.s = t.u = "";
    				}
    
    				results.push_back(t);
    				t = temp;
    				finished = false;
    			}
    		}
    		datei.close();
    	}
    	catch (const std::exception& io)
    	{
    		cout << io.what() << endl;
    	}
    }
    
    void checkline(string& line1, string& line2, struct sammlung& t)
    {
    		if (line1 == "\$Anfang:") t.d = line2;
                    if (line1 == "\$Ende:") t.s = line2;
                    ...
                    if( line1 == "XYZ:") t.tag = "Klasse1_tag";
    	        if( line1 == "ABC:") t.tag = "Klasse2_tag";
                    ....
    //               jede Zeile bekommt eine Prüfung der Art if(erster Teil) dann zuweisen des wertes an entsprechenden Wert im struct.
    
    		
    }
    

    Da ich wie bereits erwähnt noch recht neu im bereich C++ bin, fehlt es mir ein bisschen an der Art und Weise Programme zu entwerfen und ich hab auch nicht so die Idee davon, wie ich einen Ansatz finde um ein Programm performant zu entwerfen.

    Klasse 1 und 2 Erben noch von einer Basisklasse in der die Daten wie t.s1, t.d, t.s2, t.a, t.i abgespeichert werden, jedoch ist beim einlesen der Daten nicht sofort klar welcher Klasse die einzulesende Zeile zuzuordnen ist, denn dies entscheidet sich ist nach 2-3 Zeilen.

    Ich hoffe ich hab jetzt nicht allzu sehr Verwirrung gestiftet 🙈



  • @wob Wie implementiere ich denn eine Funktion die die größe eines Types ermittelt? Müsste ich dies für jeden Datentyp im Struct machen oder kann ich das für den Element im Vector berechnen lassen?
    Stehe hier ein bisschen auf dem Schlauch. Die Größe eines Vectors berechnet sich doch anhand gespeicherter Elemente * reservierte Größe?

    Hab mir jetzt sowas überlegt:

    size_t stored_size(const vector<sammlung>& t)
    {
      size_t size = 0;
      size = t.size() * t.capacity();
      return size;
    }
    


  • @prog64 sagte in Einen Vector in mehreren Dateien abspeichern, ab einer bestimmten Dateigröße.:

    @wob Wie implementiere ich denn eine Funktion die die größe eines Types ermittelt? Müsste ich dies für jeden Datentyp im Struct machen oder kann ich das für den Element im Vector berechnen lassen?

    Du müsstest das im Prinzip für jeden Typ machen. Häufig reicht aber dafür auch eine Schätzung. In einem Vector schätzt du dann eben ein paar Elemente und rechnest das hoch. Der vector selbst belegt ja Speicher der Größe capacity*sizeof(sammlung), aber wenn sammlung wieder vector, string oder ähnliches enthält, kommt da ja ggf. noch was dazu. Ist also nicht ganz trivial, sowas zu implementieren - je nachdem, wie genau man es denn wissen will. Meistens reicht ja eine grobe Schätzung. Angenommen dass deine Strings alle kurz genug für sso sind, würde ich mit vector.size() * sizeof(sammlung) arbeiten. Gut, capacity*sizeof wäre das wirklich reservierte, aber interessiert nicht vielmehr, wie groß die wirkliche Größe ist? Im Endeffekt würde ich einfach bei deiner allerersten Schätzung "N Elemente im vector = groß genug" arbeiten - wenn das wirklich nötig wäre.

    Mir ist nicht ganz klar, warum deine "Sammlung" sowohl klasse1 als auch klasse2 enthält, die auch beide noch von derselben Basisklasse erben. Du hast also in jedem deiner Sammlungsobjete alle Werte der Basisklasse doppelt drin.

    Zuletzt: 350 MB zeilenweisen Text sollte man erstens schnell einlesen können - und du braucht definitiv nicht 3.5 GB RAM, wenn du am Ende nur 83 MB rausschreibst, so wie ich das jetzt sehe. Ich schätze mal, dass du das Problem nicht besonders effizient löst 🙂



  • @prog64 sagte in Einen Vector in mehreren Dateien abspeichern, ab einer bestimmten Dateigröße.:

    @wob Wie implementiere ich denn eine Funktion die die größe eines Types ermittelt? Müsste ich dies für jeden Datentyp im Struct machen oder kann ich das für den Element im Vector berechnen lassen?
    Stehe hier ein bisschen auf dem Schlauch. Die Größe eines Vectors berechnet sich doch anhand gespeicherter Elemente * reservierte Größe?

    Hab mir jetzt sowas überlegt:

    size_t stored_size(const vector<sammlung>& t)
    {
      size_t size = 0;
      size = t.size() * t.capacity();
      return size;
    }
    

    std::vector<T>::size() ist die tatsächliche Anzahl der vorhandenen Elemente.
    std::vector<T>::capacity() ist die Anzahl Elemente die in den aktuell allokierten Speicher reinpassen würden.
    Es macht also keinen Sinn beide Werte zu multiplizieren.

    Der benötigte Speicher wäre dann eher so was wie:

    size_t stored_size(const vector<sammlung>& t)
    {
        return t.capacity() * sizeof( sammlung ); // Bzw. .size() verwenden, wenn du nur die tatächlich vorhandnene Elemente berücksichtigen willst.
    }
    

    Genau genommen hält der Vector auch noch zwei ( oder drei ) Pointer auf Anfang und Ende des Speicherbereichs. Wenn du also exakt arbeiten willst, wäre "+ 2 * sizeof( void* ) " noch zuzufügen.
    Wenn es dir auf ein paar Byte nicht ankommt, kannst du dir das sparen und einfach .size() * sizeof (T ) nehmen.



  • @It0101 sagte in Einen Vector in mehreren Dateien abspeichern, ab einer bestimmten Dateigröße.:

    Wenn du also exakt arbeiten willst, wäre "+ 2 * sizeof( void* ) " noch zuzufügen.

    Dann wohl besser + sizeof(vec) benutzen...

    @prog64: Ich denke auch, der höhere Speicherverbrauch liegt an den Strings sowie der (doppelten) Speicherung von Klasse1 bzw. Klasse2.
    Laß dir mal sizeof(std::string) ausgeben (dies ist der Speicherbedarf jedes Strings ohne die eigentlichen Stringdaten zu berücksichtigen - aufgrund von Small String Optimization (SSO) werden häufig kleine Strings direkt im string-Objekt gespeichert, anstatt den Freispeicher (Heap) zu benutzen).

    Und dann auch noch mal sizeof(sammlung) ausgeben lassen...

    Du solltest 2 unterschiedliche Klassen für die Funktion benutzen:

    • eine, welche die temporären Daten speichert
    • eine andere Klasse (wie jetzt, aber nur die wirklichen Nutzdaten enthält), welche dann die Datenklasse für den vector darstellt (bisher sehe ich in deinem Code dafür ja nur genau 4 Werte: d, temp_z, s und u - du brauchst dafür auch die beiden Klassen Klasse1 und Klasse2 nicht).

    Btw: Du solltest bessere Variablennamen benutzen...



  • Ich glaube ich packe hier einfach mal den Code rein, dann kann mir vermutlich was besser geholfen werden, ich hab eben die Variablennamen nur abgekürzt in der Hoffnung es wird deutlich was ich versuche, werde es aber nun einfach so stehen lassen 🙈

    //---------------------------------------------------------------------------------Basisklasse.h -------------------------------------------------------------------------
    #pragma once
    #include<iostream>
    
    using namespace std;
    
    class Basisklasse
    {
    private:
    
    	string datum;
    	string zeit;
    	string serverName;
    	string ipAdresse;
    	string updatedFrom;
    	string Account;
    public:
    	// Konstruktor
    	Basisklasse(string date = "", string zeit = "", string server = "", string ip = "", string updated = "", string account = "");
    
    	// Getter
    	string getDatum() const;
    	string getZeit() const;
    	string getServerName() const;
    	string getIpAdresse() const;
    	string getUpdatedFrom() const;
    	string getAccount() const;
    
    	// setter
    	void setDatum(const string& s);
    	void setZeit(const string& s);
    	void setServerName(const string& s);
    	void setIpAdresse(const string& s);
    	void setUpdatedFrom(const string& s);
    	void setAccount(const string& s);
    
    
    	// Methoden 
    	void print() const;
    };
    
    
    
    // ------------------------------------------------------------------------------ Klasse 1 -----------------------------------------------------------------------------------
    #pragma once
    #include<iostream>
    #include"Basisklasse.h"
    
    using namespace std;
    
    class Klasse1: public Basisklasse
    {
    private:
    	unsigned int contentLaenge;
    	unsigned int antwortZeit;
    	unsigned int statusCode;
    	string serverZeitStempel;
    	string referer;
    	string requestLine;
    	string userAgent;
    	string contentType;
    	string uriTranslated;
    
    public:
    	// Konstruktoren
    	Klasse1(string date = "", string time = "", string server = "", string ip = "", string updatedF = "", string account = "", // Konstruktor Basisklasse
    		unsigned int clen = 0, unsigned int rqt = 0, unsigned int stc = 0, string sTS = "", string ref = "", string rql = "", string ua="", string ctype ="", string uriT =""):
          Basisklasse(date, time, server, ip, updatedF, account), contentLaenge(clen) ... { }
    
            // Setter
    	void setContentLaenge( const unsigned int& c);
    	void setAntwortZeit( const unsigned int& c);
    	void setStatusCode(const unsigned int& c);
    	void setServerZeitstempel(const string& s);
    	void setReferer(const string& s);
    	void setRequestLine(const string& s);
    	void setUserAgent(const string& s);
    	void setContentType(const string& s);
    	void setURITranslated(const string& s);
    
    	// Getter
    	unsigned int getContentLength() const;
    	unsigned int getStatusCode() const;
    	unsigned int getRequestTime() const;
    	string getServerTimeStamp() const;
    	string getReferer() const;
    	string getRequestLine() const;
    	string getUserAgent() const;
    	string getContentType() const;
    	string getURITranslated() const;
    
    
    	// Methoden 
    	void print();
    };
    
    //-------------------------------------------------------------------------Klasse 2.h -------------------------------------------------------------------
    
    #pragma once
    #include <iostream>
    #include"Basisklasse.h"
    
    using namespace std;
    
    class Klasse2: public Basisklasse
    {
    private:
    	string sessionID;
    	unsigned int eventType;
    	unsigned int bytesFromServer;
    	unsigned int bytesToServer;
    	unsigned int messagesSent;
    	unsigned int messagesDeleted;
    	unsigned int totalOpenTime;
    
    public:
    
    	//Konstruktoren
    	Klasse2(string date = "", string time = "", string server = "", string ip = "", string updatedF = "", string account = "", // Konstruktor Basisklasse
    		string sID = "", unsigned int ev = 0, unsigned int bfs = 0, unsigned int bts = 0, unsigned int mesSent = 0, unsigned int mesDel = 0, unsigned int tOT = 0):
            Basisklasse(date, time, server, ip, updatedF, account), sessionID(sID), ... { }
    
    
    	// setter
    	void setSessionID(const string& s);
    	void setEventType(const unsigned int& c);
    	void setBytesFromServer(const unsigned int& c);
    	void setBytesToServer(const unsigned int& c);
    	void setMessagesSent(const unsigned int& c);
    	void setMessagesDeleted(const unsigned int& c);
    	void setTotalOpenTime(const unsigned int& c);
    
    	// Getter
    	string getSessionID() const;
    	unsigned int getEventType() const;
    	unsigned int getBytesFromServer() const;
    	unsigned int getBytesToServer() const;
    	unsigned int getMessagesSent() const;
    	unsigned int getMessagesDeleted() const;
    	unsigned int getTotalOpenTime() const;
    
    	// Methoden 
    	void print();
    
    };
    
    // ------------------------------------------------------------ main.cpp ----------------------------------------------------------------
    #include<iostream>
    #include<string>
    #include<vector>
    #include<fstream>
    #include<Windows.h>
    
    #include"Klasse1.h"
    #include"Klasse2.h"
    
    using namespace std;
    
    struct Sammlung
    {
    	string datum;
    	string server;
    	string updatedBy;
    	string account;
    	string ip;
    	string tag;
    	Klasse1 klasse1;
    	Klasse2 klasse1;
    };
    
    string splitString(string&);
    string ltrim(string, const string&);
    void writeFile(const string&, const vector<Sammlung>&);
    void readFile(const string&, struct Sammlung&, vector<Sammlung>&);
    void checkline(string&, string&, struct Sammlung&);
    void getFilesInDirectory( const char* cpath, vector<string>& files);
    
    
    // Argc: Argument Count
    // Argv: Argument Values (array of arrays)
    int main(int argc, char** argv)
    {
    	locale loc("german");
    	locale::global(loc);
    	string dateiname;
    	Sammlung sammlung1;
    
    	vector<Sammlung> result;
    	vector<string> sources;
    	int i = 0;
    
    	while(i < argc)
    	{
    		if (i > 1 && 4 == argc)
    		{
    			string argumente = argv[1];
    			string cpath = argv[2];
    			string outputfile = argv[3];
    
    
    			// Parameter
    			// argc muss mindestens den wert 4 haben!
    			string parameter1 = "-s";
    			string parameter2 = "-m";
    			string parameter3 = "-sd";
    			string parameter4 = "-md";
    			string parameter5 = "-h";
    
    			// einfache Dateiumwandelung
    			if (argumente == parameter1)
    			{
    				cout << "Dateiname: ";
    				getline(cin, dateiname);
    				readFile(dateiname, sammlung1, result);
    
    				cout << "Abspeichern der Daten. " << endl;
    				cout << "Dateiname: ";
    				getline(cin, dateiname);
    				writeFile(dateiname, result);
    			}
    			// mehrfache Dateiumwandlung
    			if (argumente == parameter2)
    			{
    				cpath += "\\";
    				string temp = cpath;
    				cpath += "*";
    				getFilesInDirectory(cpath.c_str(), sources);
    				
    				for (auto e : sources)
    				{
    					cout << "Bearbeiten der Datei: " << e << " ..." << endl;
    					dateiname = temp + e;
    					readFile(dateiname, sammlung1, result);
    				}
    				
    				try
    				{
    
    					writeFile(outputfile, result);
    				}
    				catch (const std::exception& e)
    				{
    					cout << e.what() << endl;
    					exit(-2);
    				}
    
    			}
    			if (argumente == parameter5)
    			{
    				cout << "HILFE:" << endl;
    				cout << "-s:\teinfaches Umwandeln in csv." << endl;
    				cout << "-m:\tmehrfaches Umwandeln in csv." << endl;
    				cout << "USAGE: -[OPTION] \"Path-to-File\" (Output-File)";
    				exit(1);
    			}
    		}
    		else if (3 == argc)
    		{
    			cout << "Namen zum abspeichern angeben!" << endl;
    			exit(-3);
    		}
    		i++;
    	}
    
    
    
    	return 0;
    }
    
    string ltrim(string s, const string& delimeter = " \t\r\n")
    {
    	return s.erase(0, s.find_first_not_of(delimeter.c_str()));
    }
    
    void splitString(string& d, string& t)
    {
    	string temp = d;
    	int t_pos = d.find_first_of(' ');
    	if (t_pos > 0)
    	{
    		d = d.substr(0, t_pos);
    		t = temp.substr(t_pos);
    		t = ltrim(t);
    	}
    }
    
    void readFile(const string& dateiname, struct Sammlung& t, vector<Sammlung>& results)
    {
    	iocs temp = t;
    	bool finished = false;
    	try
    	{
    		ifstream datei(dateiname);
    		while (datei.good())
    		{
    			string line;
    			getline(datei, line);
    
    			int pos = line.find_first_of(' ');
    			if (pos > 0)
    			{
    				string line1 = line.substr(0, pos);
    				string line2 = line.substr(pos);
    				line2 = ltrim(line2);
    				checkline(line1, line2, t);
    				if (t.updatedBy != "")
    					finished = true;
    			}
    			else if(pos < 0 && finished == true)
    			{
    				string t_zeit;
    				splitString(t.datum, t_zeit);
    				if (t.tag == "klasse1")
    				{
    					t.klasse1.setDatum(t.datum);
    					t.klasse1.setZeit(t_zeit);
    					t.klasse1.setServerName(t.server);
    					t.klasse1.setUpdatedFrom(t.updatedBy);
    					t.datum = t.server = t.updatedBy = "";
    				}
    				if (t.tag == "klasse2")
    				{
    					t.klasse2.setDatum(t.datum);
    					t.klasse2.setZeit(t_zeit);
    					t.klasse2.setServerName(t.server);
    					t.klasse2.setUpdatedFrom(t.updatedBy);
    					t.datum = t.server = t.updatedBy = "";
    				}
    
    				results.push_back(t);
    				t = temp;
    				finished = false;
    			}
    		}
    		datei.close();
    	}
    	catch (const std::exception& io)
    	{
    		cout << io.what() << endl;
    	}
    }
    
    void checkline(string& line1, string& line2, struct iocs& t)
    {
    		if (line1 == "\$TimeStamp:") t.datum = line2;
    		if (line1 == "ServerName:") t.server = line2;
    		if (line1 == "\$UpdatedBy:") t.updatedBy = line2;
    
    		if (line1 == "ContentLength:") { 
    			t.klasse1.setContentLaenge(line2); t.tag = "klasse1"; 
    		}
    		if (line1 == "ReqTimeMs:") t.klasse1.setReferer(line2);
    		if (line1 == "StatusCode:") t.klasse1.setStatusCode(stoi(line2));
    		if (line1 == "TimeStamp:") t.klasse1.setServerZeitstempel(line2);
    		if (line1 == "AuthUser:") t.klasse1.setAccount(line2);
    		if (line1 == "Partner:") t.klasse1.setIpAdresse(line2);
    		if (line1 == "Referer:") t.klasse1.setReferer(line2);
    		if (line1 == "UserAgent:") t.klasse1.setUserAgent(line2);
    		if (line1 == "RequestLine:") t.klasse1.setRequestLine(line2);
    		if (line1 == "ContentType:") t.klasse1.setContentType(line2);
    		if (line1 == "URITranslated:") t.klasse1.setURITranslated(line2);
    
    
    		if (line1 == "UserName:") {
    			t.klasse2.setAccount(line2); t.tag = "klasse2";
    		}
    		if (line1 == "SessionId:")t.klasse2.setSessionID(line2); 
    		if (line1 == "RemoteIP:") t.klasse2.setIpAdresse(line2);
    		if (line1 == "EventType:")t.klasse2.setEventType(stoi(line2));
    		if (line1 == "BytesFromServer:") t.klasse2.setBytesFromServer(stoi(line2));
    		if (line1 == "BytesToServer:") t.klasse2.setBytesToServer(stoi(line2));
    		if (line1 == "MessageSent:") t.klasse2.setMessagesSent(stoi(line2));
    		if (line1 == "MessageDeleted:")t.klasse2.setMessagesDeleted(stoi(line2));
    		if (line1 == "TotalOpenTime:")t.klasse2.setTotalOpenTime(stoi(line2));
    
    		
    }
    
    void getFilesInDirectory(const char* cpath, vector<string>& files)
    {
    	WIN32_FIND_DATAA wfd;
    	HANDLE fHandle;
    
    	fHandle = FindFirstFileA(cpath, &wfd);
    	if (fHandle != INVALID_HANDLE_VALUE)
    	{
    		do
    		{
    			if (!((wfd.cFileName[0] == '.') && ((wfd.cFileName[1] == '.' && wfd.cFileName[2] == 0) || wfd.cFileName[1] == 0)))
    			{
    				if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    					cout << "Directory: " << wfd.cFileName << " wurde gefunden!" << endl;
    				else
    				{
    					cout << "File: " << wfd.cFileName << " wird hinzugefügt." << endl;
    					files.push_back(wfd.cFileName);
    				}
    			}
    		} while (FindNextFileA(fHandle, &wfd));
    		FindClose(fHandle);
    	}
    }
    
    void writeFile(const string& dateiname, const vector<Sammlung>& results)
    {
    	string header = "\"Datum\";\"Zeit\";\"Account\";\"IP\";\"Zugriffsart\";\"RequestLine\";\"Referer\";\"UserAgent\";\"Aktivitätsdauer\";\"Bytes vom Server\";\"Bytes zum Server\";\"Messages Deleted\";\"Messages Sent\"";
    	ofstream datei(dateiname);
    	if (!datei.good())
    	{
    		cerr << dateiname << " kann nicht geschrieben werden!" << endl;
    	}
    	else
    	{
    		datei << header << endl;
    		for (int i = 0; i < results.size(); i++)
    		{
    			if (results[i].tag == "klasse1")
    			{
    				datei	<< "\"" << results[i].klasse1.getDatum() <<		"\";"
    						<< "\""	<< results[i].klasse1.getZeit() <<			"\";"
    						<< "\"" << results[i].klasse1.getAccount() <<		"\";"
    						<< "\"" << results[i].klasse1.getIpAdresse() <<	"\";"
    						<< "\"" << results[i].tag <<					"\";"
    						<< "\"" << results[i].klasse1.getRequestLine() <<	"\";"
    						<< "\"" << results[i].klasse1.getReferer() <<		"\";"
    						<< "\"" << results[i].klasse1.getUserAgent() <<	"\";";
    				datei << endl;
    			}
    			if (results[i].tag == "klasse2")
    			{
    				datei	<< "\"" << results[i].klasse2.getDatum() <<		"\";"
    						<< "\"" << results[i].klasse2.getZeit() <<		"\";"
    					<< "\"" << results[i].klasse2.getAccount() <<			"\";"
    					<< "\"" << results[i].klasse2.getIpAdresse() <<		"\";"
    					<< "\"" << results[i].tag <<						"\";"
    					<< ";;;"
    					<< "\"" << results[i].klasse2.getTotalOpenTime() <<	"\";"
    					<< "\"" << results[i].klasse2.getBytesFromServer() <<	"\";"
    					<< "\"" << results[i].klasse2.getBytesToServer() <<	"\";"
    					<< "\"" << results[i].klasse2.getMessagesDeleted() << "\";"
    					<< "\"" << results[i].klasse2.getMessagesSent() << "\";";
    				datei << endl;
    			}
    			
    
    		}
    	}
    	datei.close();
    }
    

    Ich stelle fest, das ich anscheinend mehrere Probleme habe. 😞
    Ich hatte vorgehabt die vorliegenden Daten zeilenweise einzulesen. Jedoch hab ich keine Idee wie ich dieses einlesen effizienter gestallten soll, da ich ja nicht im Vorfeld sagen kann, dass ist jetzt gerade klasse1, oder klasse2? deshalb hab ich die Basisklasse entworfen die die Daten erhält, welche in beiden klassen gleich sind. Ohne das struct ist mir dann aufgefallen, dass ich beim Zeilenweisen einlesen das Problem habe, dass ich tausend Objekte der Klasse 1 bilde, welche sich erst mit und mit befüllen, von daher hab ich ein struct angelegt, um dort alles abzuspeichern und dann am ende erst die einzelnen Klassen zu befüllen.



  • Sieht irgendwie völlig overengineered aus. Kann man das nicht in ein paar Zeilen Python lösen? Warum brauchst du überhaupt deinen Ergebnisvector?

    Kannst du nicht einfach eine Map mit jedem Datensatz erzeugen?



  • @wob Du meinst das ich beispielsweise das direkt wegschreiben sollte, sobald ein block fertig gelesen ist?
    Den Ergebnisvector habe ich deshalb, da ich die Daten ja zwischen halten wollte 😃
    Python ist jetzt nicht so wirklich das was ich programmieren wollte 🙈



  • @wob sagte in Einen Vector in mehreren Dateien abspeichern, ab einer bestimmten Dateigröße.:

    einfach eine Map

    Das möchte ich unterstreichen.
    Du hast doch mehr oder weniger einfach nur vor, ein Key-Value-Format in ein csv-Format umzuwandeln.
    Das ist mit einer map doch gut zu erledigen.

    Ohne jetzt vom Thema ablenken zu wollen, aber was halt sofort aufgefallen ist:
    Deine Basisklasse hat keinen virtuellen Destructor und auch die print-Funktion ist nicht virtuell, was wohl kaum beabsichtigt war.



  • aber eine map kann nur key - value Paare halten oder?
    Der Anfang eines Blockes ist jedoch immer "$TimeStamp: Datum Uhrzeit". Hier möchte ich Datum und Uhrzeit trennen und könnte dann den Datensatz nicht mehr als key - value speichern, da ich ja ein key - value - value Paar erhalten würde ...

    @Jockelx danke für den Hinweis 🙂



  • @prog64 sagte in Einen Vector in mehreren Dateien abspeichern, ab einer bestimmten Dateigröße.:

    aber eine map kann nur key - value Paare halten oder?
    Der Anfang eines Blockes ist jedoch immer "$TimeStamp: Datum Uhrzeit". Hier möchte ich Datum und Uhrzeit trennen und könnte dann den Datensatz nicht mehr als key - value speichern, da ich ja ein key - value - value Paar erhalten würde ...

    @Jockelx danke für den Hinweis 🙂

    Du kannst doch als Value auch ein std::pair<Date, Time> nehmen.

    vereinfacht dargestellt:

    std::unordered_map<std:.string, std::pair<Date, Time>> Data;
    Data[ MyKey ] = std::make_pair( MyDate, MyTime );
    


  • @It0101 sagte in Einen Vector in mehreren Dateien abspeichern, ab einer bestimmten Dateigröße.:

    Du kannst doch als Value auch ein std::pair<Date, Time> nehmen.

    Ich weiß nicht was du im Kopf hast; mein vorgehen wäre sowas:

    void readFile(std::vector<std::map<string, string>>& values)
    {
    // füllt zu jedem Datasatz die map mit 
    // ReqTimeMs->value
    // StatusCode->value,...
    ...
    }
    
    void writeFile(const std::vector<std::map<string, string>>& values)
    {
      std::array<std::string> header = {"ReqTimeMs", "StatusCode",...};
      for(const auto& h : headers)
      {
         for(auto& row : values)
         {
            datei << row[h] << ';';
         }
         datei << '\n';
      }
    
    

    Für spezial-Fälle wie Zeit oder letzter Eintrag dann halt gesondert beachten:

       if( h == "Zeit")
         datei << getTime(row["$TimeStamp"]) << ';';
       else if( h == "Datum")
         datei << getDate(row["$TimeStamp"]) << ';';
       else ...
    
    
    

    Edit: Die Header und Map-Keys müssen natürlich passen; sonst müssen die auch gemappt werden.



  • Vielen Dank für eure Vorschläge 🙂
    Ich habe es jetzt mal mit einer unorderd_map probiert, jedoch hab ich hier ein ähnliches Problem, was die Speicherauslastung angeht. Lese hier ca 35 MB Textfile ein und erhalte ca 750 MB im Prozesspeicher von Visual Studio (Debbuging Mode). Beim abspeichern der Daten in eine csv steigt dies sogar auf zwischenzeitlich mehrere GB, während die Ausgabe datei letztlich nur wenige 30 MB groß ist. Ich hab die Spezial-Fälle jetzt mal weggelassen ...

    Auch verstehe ich nicht ganz wie ich es beim abspeichern hinbekommen soll das die csv wie folgt aussieht:

    Datum:;Zeit:; ....
    eingelesener Wert1; Eingelesener Wert2; ...
    ...
    usw.

    #include<iostream>
    #include<string>
    #include<vector>
    #include<array>
    #include<unordered_map>
    #include<fstream>
    
    
    using namespace std;
    
    void checkline(string& line1, string& line2, vector<unordered_map<string, string>>& data)
    {
    	unordered_map<string, string> temp;
    	if (line1 == "\$TimeStamp:")
    	{
    		temp.insert({ "Datum:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "AuthUser:") {
    		temp.insert({ "Account:",line2 });
    		temp.insert({ "TAG:","Web" });
    		data.push_back(temp);
    	}
    	if (line1 == "Partner:")
    	{
    		temp.insert({ "IP:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "Referer:")
    	{
    		temp.insert({ "Referer:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "UserAgent:")
    	{
    		temp.insert({ "UserAgent:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "RequestLine:")
    	{
    		temp.insert({ "RequestLine:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "UserName:") {
    		temp.insert({ "Account:",line2 });
    		temp.insert({ "TAG:","Andere" });
    		data.push_back(temp);
    	}
    	if (line1 == "RemoteIP:")
    	{
    		temp.insert({ "IP:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "BytesFromServer:")
    	{
    		temp.insert({ "Bytes vom Server:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "BytesToServer:")
    	{
    		temp.insert({ "Bytes zum Server:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "MessageSent:")
    	{
    		temp.insert({ "Messages Sent:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "MessageDeleted:")
    	{
    		temp.insert({ "Messages Deleted:",line2 });
    		data.push_back(temp);
    	}
    	if (line1 == "TotalOpenTime:")
    	{
    		temp.insert({ "Aktivitätsdauer:",line2 });
    		data.push_back(temp);
    	}
    }
    
    
    void readFile(const string& dateiname, vector<unordered_map<string, string>>& values)
    {
    	try
    	{
    		ifstream datei(dateiname);
    		while (datei.good())
    		{
    			string line;
    			getline(datei, line);
    
    			int pos = line.find_first_of(' ');
    			if (pos > 0)
    			{
    				string line1 = line.substr(0, pos);
    				string line2 = line.substr(pos);
    				line2 = ltrim(line2);
    				checkline(line1, line2, values);
    			}
    		}
    	}
    	catch (const std::exception& e)
    	{
    		cout << e.what() << endl;
    	}
    }
    
    void writeFile(const string& dateiname,vector<unordered_map<string, string>>& data)
    {
    	try
    	{
    		ofstream datei(dateiname);
    		if (datei.good())
    		{
    			array<string,13> header = { "Datum:", "Zeit:", "Account:", "IP:","TAG:","RequestLine:","Referer:","UserAgent:","Aktivitätsdauer:","Bytes vom Server:","Bytes zum Server:","Messages Deleted:","Messages Sent:" };
    			for (const auto& h : header)
    			{
    				for (auto& rows : data)
    				{
    					datei << rows[h] << ";" ;
    				}
    				datei << endl;
    			}
    		}
    		else
    		{
    			throw "IO-Error";
    		}
    	}
    	catch (const string& e)
    	{
    		cout << e << endl;
    	}
    
    
    }
    
    int main(int argc, char** argv)
    {
    	locale loc("german");
    	locale::global(loc);
    	string dateiname;
    
    	vector<unordered_map<string, string>> Data;
    	
    	cout << "Dateipfad: ";
    	getline(cin, dateiname);
    
    	readFile(dateiname, Data);
    
    	cout << "Dateiname: ";
    	getline(cin, dateiname);
    	writeFile(dateiname, Data);
    
    	return 0;
    }
    


  • Wieso denn so viele push_back?

    Ein push_back, sobald der gesamte Datensatz eingelesen ist, nicht ein push_back pro Zeile.

    Und da du eh nichts mit den Datensätzen machst, kannst du sie auch gleich direkt rausschreiben anstatt sie in einem vector zu sammeln.



  • @wob Ich lese nicht alles ein sondern nur die Werte die in den if-Abfragen stehen, sprich ich sortiere unnötige Daten im Vorfeld aus.



  • Überlege dir die Logik in deiner bisherigen checkline-Funktion noch mal:
    du erzeugst zurzeit eine unordered_map und fügst dann genau einen Wert (Edit: Datensatz) hinzu und damit füllst du sofort den vector.

    Fülle den vector (bzw. wie schon geschrieben: schreibe gleich die Daten raus) sobald du das Ende des Datensatzes gelesen hast (dazu solltest du dann die unordered_map, statt des vector als Funktionsparameter übergeben).

    PS: Du kannst else if in der Funktion verwenden und deine string&-Parameter sollten const sein...


Log in to reply