简单的解决方案
正如其他人所指出的,直接将文件打印到流中是行不通的。打印文件内容需要打开 另一个从文件读取的流,或者将流的读取指针重新设置为开头,然后再次读取整个文件(如其他人所示)。
C++ 不会自动执行此操作,但您可以手动执行(此处为打开一个新流):
ifstream ifs("filename");
现在,将文件内容写入另一个流是一个微不足道的补充。不用写文件,只写文件缓冲区:
cout << ifs.rdbuf() << endl;
就是这样!无需循环逐行读取文件。
测试有效流
当我们讨论循环时,请注意以下列方式在循环中读取文件的代码:
while ( !file.eof() )
当出现读取错误时,此代码会产生无限循环。这在很多很多情况下都会发生。考虑例如文件在您阅读时被删除,或者有人移除了包含该文件的 USB 设备,或者该文件的格式错误。所有这些情况都会在这里创建一个无限循环。 从不只在流中测试eof。
幸运的是,这个问题的解决方案也很简单。此外,它解释了为什么您的原始代码会产生如此奇怪的结果。事实上,C++ 中的流具有到 bool-like 类型的隐式转换。由于其他地方解释的原因(提示:safe bool idiom),它实际上被转换为void*。
这使得测试流是否处于有效的非结束状态以及是否可以安全地读取变得容易。因此,我们可以适当地重新构造循环:
while (file) …
上面的代码依赖于到void* 的转换。任何非null 指针都表示有效流。现在,您的代码中也会发生同样的情况:
cout << file;
由于operator << 没有合适的重载接受流,C++ 会寻找其他重载并找到指针的重载。所以它隐含地调用这样的东西:
cout << static_cast<void*>(file);
更好的解决方案
我已经在上面解释了一个简单、有效的解决方案。但是,此解决方案需要重新打开文件并再次将其读入内存。这使所需的工作加倍。我们可以通过引入一个像流一样的新类来改善这一点,并且实际上将每个输出一次发送到两个流。这样,您可以同时将数据写入文件和标准流。无需重新读取文件。
这个类本身很简单。以下完整代码演示了一般原理:
#include <iostream>
#include <fstream>
struct sinkpair {
sinkpair(std::ostream& a, std::ostream& b) : a(a), b(b) { }
// Forward all ouputs to both streams.
template <typename T>
sinkpair& operator <<(T const& value) {
a << value;
b << value;
return *this;
}
// Explicit overload needed for manipulators such as `endl`.
sinkpair& operator <<(std::ostream& (*manip)(std::ostream&)) {
a << manip;
b << manip;
return *this;
}
private:
std::ostream& a;
std::ostream& b;
};
int main() {
std::ofstream ofs("test.txt");
sinkpair sp(std::cout, ofs);
sp << "Hello" << std::endl;
}