Große Datei möglichst schnell in kleinere Dateien aufteilen.



  • Hallo, ich habe folgendes Problem:

    Ich muss mit Dateien arbeiten die sehr groß werden können (z.B. 800 mb).
    Diese Dateien haben folgenden Aufbau (für euch vereinfacht):

    **
    1111 text
    4444 text
    2222 text
    7777 text
    2222 text
    4444 text
    1111 text
    7777 text
    2222 text
    **

    Die ersten 4 Ziffern sind die Identifikationsnummer, in jedem File gibt es unterschiedlich viele davon, bzw. sie tauchen auch unterschiedlich oft auf.
    Jetzt wollte ich aus der großen Datei viele kleine machen, in diesem Fall wären das:

    1111.txt, 2222.txt, 4444.txt, 7777.txt

    In diesen Dateien stehen dann alle Zeilen mit der zugehörigen Nr.

    Mein Ansatz:

    for(string line; getline(file, line);)
    	{				
             // Es ist egal ob newfile als Variable vor der forschleife steht
              ofstream newfile((line.substr(0,4)+".txt").c_str(), ios::app);
    	       newfile<<line<<endl;
         }
    

    Ergebnis: Es geht, dauert aber z.B. bei 600 MB über 10 Minuten, dass Problem ist scheinbar das andauerte öffnen und schließen.

    Hat jemand eine bessere Idee?



  • ich wuerds so machen:
    du oeffnest alle moeglichen Dateien (1111.txt - 9999.txt) vor der Schleife. In der Schleife - die du dann auch nur 1x durchlaufen musst - schreibst du jede Zeile in die dazugehoerige Datei... Zur Zuordnung Die_Ziffern_am_Anfang_der_Zeile => Variablenname_der_Datei waere dann z. B. ein casten nach int und dann ein switch-statement oder (evtl. die "schoenere" Loesung) eine Hashtable denkbar 🙂



  • Wäre folgender Code korrekt:

    ofstream dateien[2000];
    
    	for(int i=0; i<2000; ++i)
    	{
    		stringstream temp;
    		temp<<i<<".txt";
    		dateien[i].open(temp.str().c_str(), ios::app);
    	}
    

    Er legt irgendwie nur 504 Dateien an, danach geht die Ramnutzung immer höher, bei 300 mb habe ich abgebrochen.

    Oder soll ich das anders machen?



  • 2000 Dateien gleichzeitig aufhaben? Klingt sehr schlecht.



  • Na ja wenn das schneller wäre wäre mir das ehrlich gesagt egal.

    Ansonsten brüte ich hier gerade über alle möglichen Lösungen 😞



  • Aviator schrieb:

    Hat jemand eine bessere Idee?

    du kannst in diesem Fall anstat std::ifstream platformabhändige(wie schreibt man dieses Wort richtig?) Methoden aufruffen. z.B. ::CreateFile u.s.w.
    lese auch über File Mapping http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/file_mapping.asp



  • kannst du eine beispieldatei verfügbar machen (am besten ein kleines programm, das echt aussehende daten und davon 800M schreibt)?



  • ich fürchte, es ist optimal, für jede id eine std::queue zu nehmen und nach je 1000 (einstellbar) jelesenen zeilen die längste queue an ihre ausgabedatei dranzuhängen (mit append schreiben).

    tricks mit viel filemapping und co bringen nur faktor 2, würde ich schätzen.



  • volkard schrieb:

    ich fürchte, es ist optimal, für jede id eine std::queue zu nehmen und nach je 1000 (einstellbar) jelesenen zeilen die längste queue an ihre ausgabedatei dranzuhängen (mit append schreiben).

    tricks mit viel filemapping und co bringen nur faktor 2, würde ich schätzen.

    Hallo Volkard!

    Leider darf ich nichts rausgeben 😞

    Deine Lösung gefällt mir aber recht gut, da werde ich auch noch versuchen anzusetzen.



  • aviator schrieb:

    Leider darf ich nichts rausgeben 😞

    Du sollst ja auch nonsense daten erstellen, nur eben welche, die die struktur der echten daten haben.

    sowas verwendet man gerne zum testen 🙂



  • vielleicht pro id nen puffer von 128k, damit man kein malloc/new mehr braucht. ist der puffer voll, wird weggeschrieben. keine verkette liste mehr.



  • Würde es so auch Sinn machen?

    Du speicherst alle ostreams in einem std array.
    So musst du die nicht mehr in der for-Schleife schließen, sondern hat sie alle offen in einem Array.

    Oder du schreibst alle Zeilen in einen vector und sortierst nach der ID.
    Dann gehts du eben durch und schreibst in die Datei, die zur ID gehört.
    Kommt eine neue ID, dann machste einfach eine neue Datei auf.

    struct data_t {
      size_t id;
      string zeile;
    
      bool operator <(const data_t& rhs) {
        return id < rhs.id;
      }
    };
    vector<data_t> box;
    data_t temp;
    stringstream s;
    for(string line; getline(file, line);)
    {
      s = line.substr(0,4);
      temp.id = s; // Ich bin mir nicht sicher, ob das so geht!
      temp.zeile = line;
      box.push_back(temp);
    }
    
    sort(box.begin(), box.end();
    
    size_t oldId = 0;
    size_t newId = 0;
    ofstream out;
    for(vector<data_t>::const_iterator it = box.begin(); it != box.end(); ++it) {
      newId = it->id;
      if(newId != oldId)
      {
        if(out.is_open()) out.close();
        s = it->id;
        s += ".txt";
        out.open(s.str(), ios::app);
      }
      out << it->zeile;
      oldId = newId;
    }
    if(out.is_open()) out.close();
    
    // Ich bin mir nicht sicher, ob das alles so stimmt,
    // wie ich es geschrieben habe
    // Da hoffe ich einfach auf euere Kritik!
    
    // restliche Aufräumarbeiten
    


  • Sorry das ich mich nicht mehr gemeldet habe, aber das Problem hat mich tierisch aufgeregt. Nach jetzt stundenlangen rumprobieren bin ich zu der Erkenntnis gekommen das Volkard recht hatte, seine Lösung ist wirklich am schnellsten.



  • Wenn du das programmiert hast, würde mich der Ausschnitt brennend interessieren.



  • wie schnell ist es denn geworden?



  • hehejo schrieb:

    Würde es so auch Sinn machen?

    Nur wenn die Daten in den RAM passen, sonst nicht.

    und 800MB ist da schon etwas viel...


Anmelden zum Antworten