2013-07-26 35 views
5

我有一點點的C++問題,我無法通過在線瀏覽來解決。這裏是我的代碼(提取):如何在fstream文件之間切換而不關閉它們(同時輸出文件) - 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(); 
     } 
    } 
} 

此代碼工作正常,但我想改變它關閉每次進入while循環時間Text_File的事實。

基本上,這個程序在很多較小的文件中分割出一個很大的輸入文件。由於我的 較小的文件越來越大,執行速度越來越慢 (正常)。然後我的目標是讓這個while循環中的所有更小的文件(Text_File)在 中打開,並將fstream指針(指針?)從一個切換到另一個 。

我試圖改變爲:

... 

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(); 

... 

但它正在同Text_File無論什麼NameText是。所以我猜測fstream Text_File的指針不會改變。那麼我需要什麼?休息指針?怎麼樣?

謝謝大家!

不確定它是相關的,但我正在使用Microsoft Visual C++ 2010 Express。另外,我既不是教育也不是生活的程序員,所以如果你可以解釋它而不用太先進的話,我會很感激。

+1

如何使'File'一個數組? – wallyk

+0

我覺得像一個指向一個(或每個)已經聲明的'Text_File's將工作 –

回答

4

看起來您希望在ostream對象上玩弄filebuf s。

現在,唯一的障礙是ostreambasic_filebuf<char>不是可複製的類型,因此您不能直接將它們放入地圖(按文件名)。這是很容易工作圍繞創建一個小Holder類型:

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; 

現在完整的程序(測試)是這樣的:

#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 
} 

還有三個注意事項:

  • 文件得到buffers地圖超出範圍時自動關閉。
  • 如果您不用隱式的std::flush(如std::endl)結束行,則在切換rdbuf()時,您可能需要添加顯式刷新。
  • dummy只存在有一個ostream對象,我們可以切換

我用下面的輸入測試此緩衝器:

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 

現在,我得到以下輸出: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

修復並進一步簡化後,如果你想創建文件,如果他們不存在尚未完成。 **和**刪除了使用Boost文件系統(畢竟不需要'CheckExistance')。 ** [見所有住在Coliru](http://coliru.stacked-crooked.com/view?id=1af27de002d18cf2ded1e05f58942835-9c316e88ae784971c383263e9035353b)** – sehe

+0

謝謝Sehe! – Vince

+0

改進後的代碼可以減少對filebufs的困惑。還解決了潛在的多開放問題; (我想'map :: emplace',但是GCC還沒有它......)[Live Coliru](http://coliru.stacked-crooked.com/view?id=9c388874ad76178c7bcdd49beeae01a9-9c316e88ae784971c383263e9035353b) – sehe

1

我會做的是使用std::mapstd::unordered_map映射名稱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"; 
} 

請參閱here爲什麼我改變了while循環的條件。


您的操作系統可能無法一次打開多個打開的文件。也許你可以嘗試這樣的事情。

除地圖外,請保留打開文件的名稱列表。每次需要寫入文件時,首先在列表中搜索該文件,將其刪除並將其添加到列表的前面。如果它不在那裏,只需將它添加到列表的前面。檢查以確保文件已打開。如果不是,則嘗試打開它。如果打開它失敗,則逐個刪除列表後面的項目,關閉相應的文件到該項目,並嘗試再次打開當前文件。重複,直到打開文件成功。

這樣做將確保最常寫入的文件保留在列表的前面並保持打開狀態。不太常用的文件將移動到後面,最終被關閉。在列表中搜索文件並不是最優的(O(n)),但是由於我們正在處理寫入文件,這是一個非常昂貴的操作,所以您不應該注意到任何類型的perf命中。

+0

嗨本傑明,謝謝。我試過你的解決方案。該程序進入if條件並將nameText添加到fstream映射中,但它不會實際創建NameText文件。我的文件夾是空的。任何想法? – Vince

+0

嗨,本傑明,我試過你的代碼,它現在可以工作(我在開放行中有ios:app)。但它似乎跳過一些文件,我不明白爲什麼。我有508個文件,而不是545.當我添加行「文件[NameText] .close();」在循環的這一端,它可以工作,並且我得到了545個文件,但是與以前相比,每次都打開和關閉文件。 – Vince

+0

@Vince:我已經添加了一個可能的解釋和解決方案。 –

0

請注意,Text_File是一個變量,並且像所有變量一樣,可以有多個具有相同類型的變量。如果您需要管理多個不同的文件,您甚至可以在任何標準容器中使用std::fstream,例如std::vectorstd::map。另外,你應該考慮把你的代碼分解成更小更易於管理的部分。例如,您可以創建一個以std::fstream&作爲參數的函數。這允許程序的其餘部分控制在任何給定時間使用哪個std::fstream&。我強烈建議你看看不同的設計選項來幫助組織你的代碼。

2

注意:它看起來像你的Text_File超出範圍。我想你在代碼中聲明瞭其他地方。所以,這條線是沒用的:

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

訪問多個文件流,你可以使用這個簡單的類,它利用std::map數據結構:

#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(); 

然後,訪問文件一樣簡單如:

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

就是這樣。

+0

-1您不能將'fstream'對象放入'map'中(因爲它們不可複製/移動)。看到我的答案,一個經過全面測試的解決方案 – sehe

+0

哎呀,我剛剛試圖在gcc中編譯此代碼,並且您是正確的......但是,Visual C++版本的std :: map '編譯好,沒有錯誤,沒有警告。 – podkova

+0

有趣的:)它不應該編譯。雖然因爲C++ 11'fstream'應該是可移動的。雖然我正在撤銷我的-1,因爲你已經測試過它,它可能適用於某人。 – sehe

0

存在檢查語句沒有影響 - 正如已經提到的那樣。也許你的意圖是做這樣的事情:

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(); 
} 

if語句範圍內的fstream將隱藏你必須在外部作用域中的fstream。另外,關閉是可選的 - 當流程超出範圍時,流將被關閉。

相關問題