用C ++ fstream读取文件的“正确”方法是什么?

时间:2013-03-08 15:04:52

标签: c++ file-io iostream

我正在使用标准的C ++ fstreams库,我想知道正确使用它的方式是什么。根据经验,我想出了一个小的使用协议,但我不是很确定。为了简单起见,我们假设我只想读取一个文件,例如,过滤其内容并将其放在另一个文件上。我的例程大致如下:

  • 我声明了一个本地istream i("filename")变量来打开文件;
  • 我检查i.good()i.is_open()以处理打开时出现问题的情况,例如,因为该文件不存在;之后,我认为该文件存在,我没事;
  • 我呼叫i.peek()然后再呼叫i.good()i.eof()以排除文件为空的情况;之后,我认为我实际上有一些东西要读;
  • 我使用>>或其他任何东西来阅读文件的内容,eof()来检查我是否已经结束;
  • 我没有明确关闭文件 - 我依赖RAII并保持我的方法尽可能简短和连贯。

这是一个理智(正确,最小)的例程吗?在否定的情况下,你会如何解决它?请注意,我考虑比赛 - 同步是另一回事。

2 个答案:

答案 0 :(得分:4)

我会消除peek / good / eof(你的第三步)。只需尝试读取您的数据,并检查尝试读取是成功还是失败。同样,在第四步中,只需检查您的尝试读取是否成功。

典型代码如下:

std::ifstream i("whatever");

if (!i)
    error("opening file");

while (i >> your_data)
    process(your_data);

if (!i.eof())
   // reading failed before end of file

答案 1 :(得分:3)

它比你描述的简单。前两个步骤很好(但如果你按照我的其他建议,第二步是没有必要的)。然后你应该尝试提取,但是使用提取作为循环或if语句的条件。例如,如果文件被格式化为所有相同格式的一系列行(或其他分隔序列),您可以这样做:

std::string line;
while (std::getline(i, line)) {
  // Parse line
}

循环体只会在行提取工作时执行。当然,您需要检查循环内线的有效性。

如果您要对流进行一系列提取或其他操作,则可以将它们置于if条件下,如下所示:

if (i >> some_string &&
    i.get() == '-' &&
    i >> some_int) {
  // Use some_string and some_int
}

如果第一次提取失败,i.ignore()由于&&的短路评估而无法执行。 if语句的主体只有在两次提取都成功时才会执行。如果你有两个提取,你当然可以链接它们:

if (i >> some_string >> some_int) {
  // Use some_string and some_int
}

如果第一个提取失败,则不会发生链中的第二次提取。提取失败会使流处于一种状态,其中所有后续提取也会自动失败。

出于这个原因,将流操作置于if条件之外,然后检查流的状态也很好:

i >> some_string >> some_int;
if (i) {
  // Use some_string and some_int
}

使用这两种方法,您不必检查流的某些问题。检查eof()的流并不一定意味着下一次读取将失败。一个常见的情况是当人们使用以下不正确的提取循环时:

// DO NOT DO THIS
while (!i.eof()) {
  std::getline(i, line)
  // Do something with line
}

大多数文本文件以文本编辑器隐藏的最后一行新行结束。当您从文本文件中读取行时,在最后一次迭代中,您还没有到达文件末尾,因为仍然需要读取\n。因此循环继续,尝试提取不存在的下一行并拧紧。人们经常将其视为“两次读取文件的最后一行”。