单个文件如何同时链接到两个不同的流?

时间:2015-04-14 06:25:15

标签: c++

 ifstream  fin("test.txt");
  ofstream  fout ("test.txt");

如果在同一程序中写入上述两行,则不会产生任何错误或警告。 但是我们如何同时写入和读入同一个文件。 这是怎么回事?

3 个答案:

答案 0 :(得分:0)

C ++标准中没有任何内容排除这种情况 - 请求只是传递给操作系统,操作系统可能允许也可能不允许。

例如,在某些OS /文件系统组合中,如果您要创建与现有文件同名的文件,操作系统会隐藏现有文件的目录条目/条目,以便其他应用程序无法打开它,但现有进程正在使用它将能够继续这样做;当他们全部关闭文件时将被删除。同时,可以使用相同的名称创建一个新文件,然后打开该文件的任何应用程序都将看到已刷新到该新文件的任何内容。

在其他系统上,可能会生成某种“正在使用”或“已锁定”的错误消息,导致fail中的bad / std::ofstream状态。

您应该始终测试文件流创建是否成功,例如:

if (std::ifstream in("filename.txt"))
    ...use in...
else
    std::cerr << ... or throw std::runtime_error(...)

答案 1 :(得分:0)

因此,正如评论所说,编译器本身并不真正知道你想要实现的目标(例如,你肯定会想要与此类似的东西):

// With exceptions for file operations enabled:

try
{
   ifstream fin("test.txt");
   return true;
}
catch(...)
{
   try
   {
     ofstream fout("test.txt"); 
   }
   catch(...)
   {
     cout << "Can't create file";
     return false;
   }
   return true;
}

现在,这将(以一种相当愚蠢和复杂的方式)检查文件是否存在或是否可以创建。你想让编译器也抱怨吗?编译器必须&#34;理解&#34;当使用哪个文件时要遵循的很多逻辑,并且看到两者都不能同时使用(是的,编译器可能会理解在这种情况下,因为我们在{之后立即返回' {1}}使用 - 使代码更复杂,并且它赢了。

现在,您无法使用以下代码读取/写入同一文件:

fin

根据操作系统(以及下面图层中使用的标志),您可以实际执行这两行而不会出现错误 - 在Unix中,打开的文件在系统中保持打开状态,即使其他内容已删除文件(第二行将执行),因此您可以阅读&#34; old&#34;文件,并写入新的。那当然不是&#34;同时读取和写入同一个文件&#34;,但是读取一个文件并写入另一个文件。在大多数非Unix文件系统中,这不起作用,因为操作系统将对两个调用使用相同的目录条目,并打开现有文件,然后第二个调用将使文件为空或失败(取决于操作系统,等等)。

如果您真的想要读取和写入相同的文件,解决方案是:

  1. 打开文件时使用ifstream fin("test.txt"); ofstream fout ("test.txt"); 作为标志。
  2. 使用临时文件作为输出端,并在文件完成后重命名该文件&#34;。由于文本文件的大多数类型的更改都要求文件被重写&#34; [除了将内容添加到最后],这通常是首选方法。然后,当完成更改时,将新文件重命名为旧文件(首先使用原始文件的ios_base::in|ios_base::out)。这也意味着即使您的程序崩溃,您总是至少有一个完整的文件(尽管名称错误)。

答案 2 :(得分:0)

编译器没有理由知道你做错了什么,它不会记住你在程序中使用过的所有文件名。此外,如果文件名来自用户输入或仅来自变量,编译器永远不会猜测它们将是相同的:

ifstream fin(s1);
ofstream fout (s2); // is s1==s2?

事实上,你的情况(硬编码文件名)是非常不寻常的情况,所以我不认为这样的警告在一般情况下是有用的。

而且,除此之外,此代码有时甚至可以正常工作。

例如,如果test.txt是命名管道,则可以安全地同时打开它以进行读取和写入。您仍然需要做一些解决方法来防止阻塞,最简单的方法是在不同的线程中打开管道。但你可以安全地想象一下命名管道操作不会阻塞的情况(我不知道这是否可能在某些操作系统选项等的流行操作系统中,但无论如何你总能想象这样的操作系统),在这种情况下如下代码应该没有任何问题:

// (for imaginary OS where named pipe operations do not block)
ifstream fin("test.fifo");
ofstream fout("test.fifo");
fout << 42;
int x;
fin >> x; // produces 42

因此编译器没有理由警告或发出错误。如果要在运行时检查文件打开是否成功,请检查它。