2013-07-26 3 views
5

Ich habe ein kleines C++ - Problem, das ich nicht durch Online-Surfen lösen konnte. Hier ist mein Code (extrahiert):Wie kann ich zwischen fstream-Dateien wechseln, ohne sie zu schließen (Simultane Ausgabedateien) - C++

if(File.is_open()) { 
    while(!File.eof()) { 
     i++; 
     getline(File,Line); 
     if(i>=2) {    //Skip Headers 
      int CharCount=0; 
      for(int CharPosition=0; CharPosition<Line.size(); CharPosition++)      { 
       if(Line[CharPosition]==',') { 
        Length=CharPosition; 
        break; 
       } 
      } 
      NameText=Line.substr(0,Length); 
      Path= Path_Folder + "\\" + NameText + ".csv"; 
      if(!CheckExistance(Path.c_str())) { 
       fstream Text_File; 
      } 
      Text_File.open(Path, fstream::in | fstream::out | fstream::app); 
      Text_File<<Line<<"\n"; 
      Text_File.close(); 
     } 
    } 
} 

Dieser Code funktioniert gut, aber ich möchte die Tatsache ändern, dass sie die Text_File schließt jedes Mal in der while-Schleife geht.

Grundsätzlich teilt dieses Programm eine große Eingabedatei in viele kleinere Dateien auf. Da meine kleinere Dateien immer größer werden, wird die Ausführung langsamer und langsamer (normal). Mein Ziel ist es dann, alle kleineren Dateien (Text_File) in diese while-Schleife zu lassen und nur den fstream-Zeiger (Zeiger?) Von einem auf anderen zu schalten.

ich versuchte, so zu ändern:

... 

NameText=Line.substr(0,Length); 
Path= Path_Folder + "\\" + NameText + ".csv"; 

if(!CheckExistance(Path.c_str())) { 
    fstream Text_File; 
} 

if(!Text_File.open()) { 
    Text_File.open(Path, fstream::in |fstream::out | fstream::app); 
} 

Text_File<<Line<<"\n"; 
\\Text_File.close(); 

... 

Aber es funktioniert auf dem gleichen Text_File egal was NameText ist. Ich vermute also, dass sich der Zeiger des fstream Text_File nicht ändert. Was muss ich dann sein? Den Zeiger ruhen lassen? Wie?

Vielen Dank, alle!

Nicht sicher, dass es relevant ist, aber ich arbeite mit Microsoft Visual C++ 2010 Express. Darüber hinaus bin ich kein Programmierer weder durch Bildung noch durch das Leben, also wenn Sie es ohne zu hoch entwickelte Wörter erklären können, werde ich schätzen.

+1

Wie wäre es 'File' ein Array zu machen? – wallyk

+0

Ich fühle mich wie ein Zeiger auf eine (oder jede) der bereits deklarierten 'Text_File' würde funktionieren –

Antwort

4

Es sieht so aus, als ob Sie die filebuf s auf einem ostream Objekt jonglieren möchten.

Jetzt ist das einzige Hindernis, dass ostream oder basic_filebuf<char> keine kopierbaren Typen sind, so dass Sie sie nicht direkt in eine Karte (nach Dateiname) einfügen können. Dies ist leicht durch die Schaffung eines kleinen Holder Art umgingen:

#include <fstream> 
#include <sstream> 
#include <iostream> 
#include <map> 
#include <memory> 

const std::string Path_Folder = "."; 

int main() 
{ 
    std::istream& File  = std::cin; // just for example 
    std::filebuf dummy; 
    std::ostream TextFile(&dummy); 

    struct Holder { 
     Holder(std::string const& path) 
      : buf(std::make_shared<std::filebuf>()) 
     { 
      buf->open(path.c_str(), std::ios::out | std::ios::app); 
     } 
     std::shared_ptr<std::filebuf> buf; 
    }; 

    std::map<std::string, Holder> buffers; 
    int i = 0; 

    std::string Line; 
    while(getline(File, Line)) 
    { 
     if (i++<2) 
      continue; //Skip Headers 

     auto NameText = Line.substr(0, Line.find(',')); 
     auto Path = Path_Folder + '/' + NameText + ".csv"; 

     // open, only if not allready opened 
     auto found = buffers.find(NameText); 
     if (end(buffers) == found) 
      found = buffers.insert({ NameText, Path }).first; 

     TextFile.rdbuf(found->second.buf.get()); 

     TextFile << Line << std::endl; // notice implicit std::flush in std::endl 
    } 

    // all files are automatically closed here 
} 

Drei weitere Hinweise:

  • Dateien erhalten

    struct Holder { 
        Holder(std::string const& path) 
         : buf(std::make_shared<std::filebuf>()) 
        { 
         buf->open(path.c_str(), std::ios::out | std::ios::app); 
        } 
        std::shared_ptr<std::filebuf> buf; 
    }; 
    
    std::map<std::string, Holder> buffers; 
    

    nun das komplette Programm (getestet) würde wie folgt aussehen automatisch geschlossen, wenn die Karte den Gültigkeitsbereich verlässt.

  • Sie müssen möglicherweise explizite Spülungen hinzufügen, wenn Sie rdbuf() wie folgt ändern, wenn Sie Ihre Zeilen nicht mit einem impliziten std::flush (wie mit std::endl) beenden.
  • dummy nur ein ostream Objekt zu haben, besteht, dass wir den Puffer von

umschalten können ich dies mit folgenden Eingabe getestet:

Header Row #1 
Header Row #2 
Jack,1,some data 
Jill,2,some more data 
Jack,3,not reopening :) 
Jill,4,jill still receiving output 
Romeo,5,someone else reporting 

Jetzt habe ich die folgende Ausgabe: see it live at Coliru

/tmp$rm *.csv
/tmp$make && ./test < input.txt && tail *.csv

g++ -std=c++11 -Wall -g test.cpp -o test 
==> Jack.csv <== 
Jack,1,some data 
Jack,3,not reopening :) 

==> Jill.csv <== 
Jill,2,some more data 
Jill,4,jill still receiving output 

==> Romeo.csv <== 
Romeo,5,someone else reporting 
+0

Behoben und weiter vereinfacht nachdem Sie realisiert haben, dass Dateien erstellt werden sollen, wenn sie noch nicht existieren. ** Und ** entfernte die Verwendung von Boost-Dateisystem (da 'CheckExistance' schließlich nicht benötigt wurde). ** [Siehe alles live in Coliru] (http://coliru.stacked-crooked.com/view?id=1af27de002d18cf2ded1e05f58942835-9c316e88ae784971c383263e9035353b) ** – sehe

+0

Danke Sehe! – Vince

+0

Verbesserter Code, um über Filebufs weniger verwirrt zu werden. Auch festgelegtes potenzielles Multi-Open-Problem; (Ich wollte 'map :: emplace', aber GCC hat es noch nicht ...) [Live on Coliru] (http://coliru.stacked-crooked.com/view?id=9c388874ad76178c7bcdd49beaea01a9-9c316e88ae784971c383263e9035353b) – sehe

1

Was würde ich tun, ist verwenden std::map oder std::unordered_map Namen zuzuordnen Objekte fstream.

map<string, fstream> files; 

... 

while(getline(File,Line)) // don't use while(File.eof()) 
{ 
    ... 

    if(files.count(NameText) == 0) // checks for the existence of the fstream object 
    {    
     files[NameText].open(Path, fstream::in | fstream::out); 
    } 

    files[NameText] << Line << "\n"; 
} 

Siehe here warum ich die Bedingung für die while-Schleife verändert.


Ihr Betriebssystem kann Probleme haben, so viele geöffnete Dateien gleichzeitig zu haben. Vielleicht könntest du so etwas versuchen.

Halten Sie neben Ihrer Karte eine Liste der Namen der geöffneten Dateien. Jedes Mal, wenn Sie in eine Datei schreiben müssen, suchen Sie zunächst in Ihrer Liste danach, entfernen Sie sie und fügen Sie sie am Anfang der Liste hinzu. Wenn es nicht dort ist, fügen Sie es einfach an den Anfang der Liste. Überprüfen Sie, ob die Datei geöffnet ist. Wenn nicht, dann versuche es zu öffnen. Wenn das Öffnen fehlschlägt, entfernen Sie nacheinander Elemente aus der Liste, schließen Sie die entsprechende Datei für dieses Element und versuchen Sie, die aktuelle Datei erneut zu öffnen. Wiederholen Sie dies, bis das Öffnen der Datei erfolgreich ist.

Dadurch wird sichergestellt, dass die am häufigsten in Dateien geschriebenen am Anfang der Liste bleiben und offen bleiben. Die weniger häufig geschriebenen Dateien werden nach hinten verschoben und schließlich geschlossen. Die Suche nach der Datei in der Liste ist nicht optimal (O (n)), aber da wir es hier mit dem Schreiben von Dateien zu tun haben, was eine viel teurere Operation ist, sollten Sie keinen Perf-Treffer bemerken.

+0

Hallo Benjamin, danke. Ich habe deine Lösung versucht. Das Programm geht in die if-Bedingung über und fügt der flstream-Map den Namenstext hinzu, aber es erstellt nicht physisch die NameText-Datei. Mein Ordner ist leer. Irgendeine Idee? – Vince

+0

Hallo Benjamin, Ich habe deinen Code ausprobiert und es funktioniert jetzt (ich hatte iOS: App in der offenen Leitung). Aber es scheint einige Dateien zu überspringen und ich verstehe nicht warum. Ich habe 508 Dateien anstelle von 545. Wenn ich die Zeile "files [NameText] .close();" an diesem Ende der Schleife, während es funktioniert, und ich bekomme 545 Dateien, aber dann bin ich an der gleichen Stelle als zuvor: Die Dateien werden jedes Mal geöffnet und geschlossen. – Vince

+0

@Vince: Ich habe eine mögliche Erklärung und Lösung hinzugefügt. –

0

Beachten Sie, dass Text_File eine Variable ist und Sie wie alle Variablen mehr als eine mit demselben Typ haben können. Wenn Sie mehrere verschiedene Dateien verwalten müssen, können Sie sogar std::fstream in einem der Standardcontainer wie std::vector oder std::map verwenden. Außerdem sollten Sie in Betracht ziehen, Ihren Code in kleinere, besser zu verwaltende Teile aufzuteilen. Zum Beispiel können Sie eine Funktion erstellen, die einen std::fstream& als Parameter verwendet. Dies ermöglicht dem Rest des Programms zu steuern, welche std::fstream& zu einem bestimmten Zeitpunkt verwendet wird. Ich schlage vor, dass Sie verschiedene Design-Optionen betrachten, um Ihren Code zu organisieren.

2

Hinweis: Es sieht so aus, als ob Ihr Text_File außerhalb des Geltungsbereichs liegt. Ich nehme an, du hast es irgendwo anders im Code deklariert. Also, diese Zeile ist nutzlos:

if(!CheckExistance(Path.c_str())){fstream Text_File;} 

mehrere Datei zuzugreifen Ströme können Sie diese einfache Klasse verwenden, die die std::map Datenstruktur verwendet:

#include <iostream> 
#include <map> 
#include <string> 
#include <fstream> 

class StreamWriter 
{ 
    typedef std::map<std::string, std::fstream> StreamMap; 
    static StreamMap Files; 

public: 
    static std::fstream& GetFile(const std::string& filename) 
    { 
     std::fstream& stream = Files[filename]; 
     if (!stream.is_open()) 
     { 
      stream.open(filename, std::fstream::in 
        | std::fstream::out | std::fstream::app); 
     } 
     return stream; 
    } 
}; 

StreamWriter::StreamMap StreamWriter::Files = StreamWriter::StreamMap(); 

Dann Zugriff auf Dateien ist so einfach wie:

StreamWriter::GetFile("C:/sample1.txt") << "test"; 

Das ist es.

+0

-1 Sie können 'fstream' Objekte nicht so in eine' Karte' einfügen (weil sie nicht kopierbar/beweglich sind). Siehe meine Antwort für eine vollständig getestete Lösung – sehe

+0

Autsch, Ich habe gerade versucht, diesen Code in gcc zu kompilieren, und Sie haben Recht ... Allerdings Visual C++ - Version von 'std :: map 'kompiliert gut, keine Fehler, keine Warnungen. – podkova

+0

Interessant :) Es sollte nicht kompilieren. Obwohl seit C++ 11 'fstream' soll beweglich sein. Ich annulliere meine -1 aber, weil du es getestet hast und es könnte für jemanden funktionieren. – sehe

0

Die Existenzprüfung Anweisung hat keine Auswirkungen - wie bereits erwähnt. Vielleicht war Ihre Absicht, so etwas zu tun:

if(!CheckExistance(Path.c_str())) { 
    fstream Text_File; 

    Text_File.open(Path, fstream::in | fstream::out | fstream::app); 
    Text_File<<Line<<"\n"; 
    Text_File.close(); 
} 

Die fstream im Rahmen der if-Anweisung, die wird man verstecken Sie in dem äußeren Umfang haben muss. Schließen ist optional - der Stream wird geschlossen, wenn er nicht mehr verfügbar ist.

Verwandte Themen