2

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

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

这是一个理智的(正确的,最小的)例行程序吗?在消极的情况下,你将如何解决它?请注意,我考虑种族 - 同步是另一回事。

4

2 回答 2

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
于 2013-03-08T15:24:04.740 回答
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
}

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

于 2013-03-08T15:23:28.857 回答