简体   繁体   English

如何在不关闭它们的情况下切换fstream文件(同时输出文件) - C ++

[英]How can I switch between fstream files without closing them (Simultaneous output files) - C++

I have a little C++ issue that I couldn't solve by browsing online. 我有一点C ++问题,我无法通过在线浏览解决。 Here is my code (extracted): 这是我的代码(摘录):

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

This code is working fine, but I would like to change the fact that it closes the Text_File every time it goes in the while loop. 这段代码工作正常,但我想改变它每次进入while循环时关闭Text_File的事实。

Basically, this program split a big input file in a lot of smaller files. 基本上,这个程序在很多较小的文件中分割出一个大的输入文件。 As my smaller files get bigger and bigger, the execution gets slower and slower (normal). 随着我的小文件变得越来越大,执行越来越慢(正常)。 My goal is then to let all the smaller files ( Text_File ) opened in this while loop and just switch the fstream pointer (pointer?) from one to another. 我的目标是让所有较小的文件( Text_File )在这个while循环中打开,然后将fstream指针(指针?)从一个切换到另一个。

I tried to change as: 我试图改变为:

...

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

...

But it is working on the same Text_File no matter what NameText is. 但无论NameText是什么,它都在使用相同的Text_File So I am guessing that the pointer of the fstream Text_File doesn't change. 所以我猜测fstream Text_File的指针不会改变。 What do I need to be then? 那我需要做什么? Rest the pointer? 把指针放在一边? How? 怎么样?

Thank you, all! 谢谢你们!

Not sure it is relevant but I am working with Microsoft Visual C++ 2010 Express. 不确定它是否相关,但我正在使用Microsoft Visual C ++ 2010 Express。 In addition, I am not a programmer neither by education nor by living, so if you can explain it without too advanced words, I'll appreciate. 另外,我既不是教育也不是生活的程序员,所以如果你能用不太先进的词语来解释它,我会很感激。

It looks like you would like to juggle the filebuf s on an ostream object. 看起来你想要在ostream对象上处理filebuf

Now, the only obstacle is that ostream or basic_filebuf<char> aren't copyable types, so you can't put them into a map (by filename) directly. 现在,唯一的障碍是ostreambasic_filebuf<char>不是可复制类型,因此您不能直接将它们放入地图(通过文件名)。 This is easily worked around by creating a little Holder type: 通过创建一个小型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;

Now the complete program (tested) would look like this: 现在,完整的程序(已测试)将如下所示:

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

Three more notes: 还有三个笔记:

  • files get automatically closed when the buffers map goes out of scope. buffers映射超出范围时,文件会自动关闭。
  • you might need to add explicit flushes when switching rdbuf() like this, if you don't end your lines with an implicit std::flush (like with std::endl ). 你可能需要在切换这样的rdbuf()时添加显式刷新,如果你不使用隐式std::flush结束你的行(就像使用std::endl )。
  • dummy only exists to have an ostream object that we can switch the buffer of dummy只存在一个我们可以切换缓冲区的ostream对象

I tested this with the following input: 我用以下输入测试了这个:

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

Now, I got the following output: see it live at Coliru 现在,我得到了以下输出: 在Coliru看到它

/tmp$ rm *.csv /tmp$ rm *.csv
/tmp$ make && ./test < input.txt && tail *.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

Note: it looks like your Text_File is out of scope. 注意:看起来你的Text_File超出了范围。 I guess you declared it somwhere else in the code. 我猜你在代码中的其他地方声明了它。 So, this line is useless: 所以,这条线是没用的:

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

To access multiple file streams you can use this simple class which utilizes the std::map data structure: 要访问多个文件流,您可以使用这个使用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();

Then, access to files is as simple as: 然后,访问文件就像下面这样简单:

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

That's it. 而已。

What I would do is use std::map or std::unordered_map to map names to fstream objects. 我要做的是使用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";
}

See here for why I changed the condition for the while loop. 请参阅此处了解为什么我更改了while循环的条件。


Your OS may have trouble having that many open files at once. 您的操作系统可能无法同时拥有多个打开的文件。 Perhaps you could try something like this. 也许你可以尝试这样的事情。

Alongside your map, keep a list of the names of the files that are open. 在地图旁边,保留打开的文件名称列表。 Each time you need to write to a file, first search for it in your list, remove it and add it to the front of the list. 每次需要写入文件时,首先在列表中搜索它,将其删除并将其添加到列表的前面。 If it's not there, just add it to the front of the list. 如果不存在,只需将其添加到列表的前面即可。 Check to make sure the file is open. 检查以确保文件已打开。 If it's not, then try to open it. 如果不是,那么试着打开它。 If opening it fails, then one by one, remove items from the back of the list, close the corresponding file to that item, and try to open the current file again. 如果打开它失败,则逐个删除列表后面的项目,关闭该项目的相应文件,并尝试再次打开当前文件。 Repeat until opening the file succeeds. 重复,直到打开文件成功。

Doing this will ensure that the most frequently written to files stay at the front of the list and remain open. 这样做可确保最常写入文件的文件保留在列表的前面并保持打开状态。 The less frequently written files will move to the back, and eventually be closed. 编写频率较低的文件会移到后面,最终会被关闭。 The search for the file in the list is not optimal (O(n)), but since we're dealing with writing to files here, which is a much more expensive operation, you shouldn't notice any kind of perf hit. 在列表中搜索文件不是最佳的(O(n)),但由于我们在这里处理文件写入,这是一个更昂贵的操作,你不应该注意到任何类型的性能。

You are trying to reuse the Text_File fstream. 您正在尝试重用 Text_File fstream。 To do this, you have to do a close() to flush the stream, after you are done writing to a csv file. 为此,在完成写入csv文件后,必须执行close()来刷新流。 Please see this question: C++ can I reuse fstream to open and write multiple files? 请看这个问题: C ++可以重用fstream来打开和写入多个文件吗?

Also: Here's my Google search for this question: http://goo.gl/Oy5KKM 另外:这是我在谷歌搜索这个问题: http//goo.gl/Oy5KKM

Note that Text_File is a variable and like all variables you can have more than one with the same type. 请注意, Text_File是一个变量,就像所有变量一样,您可以拥有多个具有相同类型的变量。 If you need to manage several different files, you can even use std::fstream in any of the standard containers such as std::vector or std::map . 如果您需要管理多个不同的文件,甚至可以在任何标准容器中使用std::fstream ,例如std::vectorstd::map Also, you should consider breaking your code down into smaller more manageable parts. 此外,您应该考虑将代码分解为更小,更易于管理的部分。 For example, you can create a function which takes an std::fstream& as a parameter. 例如,您可以创建一个以std::fstream&作为参数的函数。 This allows the rest of the program to control which std::fstream& is used at any given time. 这允许程序的其余部分控制在任何给定时间使用哪个std::fstream& I strongly suggest that you look at different design options to help organize your code. 我强烈建议您查看不同的设计选项以帮助组织代码。

The existence check statement has no effect - as mentioned already. 存在检查语句没有效果 - 如前所述。 Perhaps your intention was to do something like this: 也许你打算做这样的事情:

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

The fstream within the scope of if statement will hide the one you must have in the outer scope. if语句范围内的fstream将隐藏外部作用域中必须具有的那个。 Also, close is optional - stream will be closed when it goes out of scope. 此外,close是可选的 - 当流超出范围时,流将被关闭。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM