C++ std::fstream getline() 在第一行长行后停止读取文件

C++ std::fstream getline() stops reading file after first long line

提问人:Vladlen 提问时间:8/30/2023 最后编辑:genpfaultVladlen 更新时间:8/31/2023 访问量:67

问:

我使用 std::fstream 通过成员函数 getline() 逐行读取文本文件。它使用有限大小的缓冲区,有时输入上的行比缓冲区长。第一次读取长行停止读取数据后,调试代码后让我感到惊讶。文档指出,读取长行会设置错误标志,**它被描述为正常情况,而不是运行时错误或致命故障 (https://cplusplus.com/reference/istream/istream/getline/)。文档没有注意到这个标志,也没有描述如何处理长线。getline()failbit

你知道任何有据可查的方法,如何排长队吗?它应该是,因为这种行为会影响所有类 std::fstream,但我从未看到任何描述或讨论。

程序示例:

#include <fstream>
#include <iostream>
#include <string>

int main(){
 char buffer[10];
 std::string filename{"test.txt"};
 auto file=std::fstream(filename, std::ios_base::in | std::ios_base::binary );
 
 if(file.is_open()){
  size_t readBites{4};
   file.getline(buffer,readBites);
   auto length=std::strlen(buffer);
   auto lengthStream=file.gcount();
   std::cout << "Read "<<length<<"/"<<lengthStream<<":"<<buffer<<std::endl;
   file.getline(buffer,readBites);
   length=std::strlen(buffer);
   lengthStream=file.gcount(); 
   std::cout << "Read "<<length<<"/"<<lengthStream<<":"<<buffer<<std::endl;
 }
 return 0;
}

如果 test.txt 文件包含,例如:

1234567890
ABCDEFGHIJK

我期望输出(如在 C 中):fgets(...)

Read 3/3:123
Read 3/3:456

但它确实是:

Read 3/3:123
Read 0/0:

我使用有限大小的缓冲区,因为这是客户端软件的要求,调用此代码,它可以是任何大小,并在短缓冲区中处理长行。

C++ Fstream GetLine

评论

1赞 Homer512 8/30/2023
您确实知道 std::string 有一个 getline 版本,它为整行分配内存,对吧?
1赞 273K 8/30/2023
任何尝试阅读手册 std::basic_istream<CharT,Traits>::getline 的尝试?如果已提取 count - 1 个字符,则调用 SetState(failbit)。
0赞 Ted Lyngmo 8/30/2023
“......逐行读取文本文件“ - 那为什么呢?std::ios_base::binary
0赞 Vladlen 8/30/2023
@Homer512我在最后一句话中解释的那样,要求是客户端,调用约定,我必须感觉到提供的缓冲区。一切都有效,直到没有在输入上测试长线。我重新阅读了文档,没有看到任何明确的解释,在这种情况下如何处理,为什么我之前忽略了它。
0赞 Vladlen 8/30/2023
@273K 是的,这正是我在第 4 行中描述的 - 设置了标志。接下来呢?没有描述,如何在这种情况下以记录的方式退出。比你的链接,虽然它有相同的getline描述,在我的链接中。

答:

4赞 Remy Lebeau 8/30/2023 #1

正如您链接到的文档所说:

如果函数未提取任何字符,或者一旦 (n-1) 个字符已写入 s,则未找到分隔符,则设置 failbit 标志。

因此,如果尝试读取的数据超过缓冲区可以容纳的数据,则会设置流的状态:failbit

要么未找到分隔字符,要么根本没有提取任何字符(因为文件末尾在第一个字符之前,或者因为构造失败)。sentry

此时,流将不再读取任何数据:

在内部,该函数通过首先构造一个对象(设置为 true)来访问输入序列。然后(如果好),它从其关联的流缓冲区对象中提取字符,就像调用其成员函数或 一样,最后在返回之前销毁该对象。sentrynoskipwssbumpcsgetcsentry

由于流不再处于状态,因此它会停止提取字符。您必须清除()流才能将其恢复到状态。goodgood

评论

0赞 Vladlen 8/30/2023
这是有道理的。你能指出这个文档吗?我浏览了带有文档的 C++ 网站,但从未找到这样的声明。
0赞 Vladlen 8/30/2023
这是否意味着,每次使用 fteam 的 getline() 之后都必须检查标志,比较请求的缓冲区长度和接收长度,并通过 clear() 清除标志?任何地方都没有描述它。这不是一个糟糕的过程(就像在 C 中检查 fgetc(...) != EOF 的每个返回值一样),但至少在我看的地方没有提到。
1赞 273K 8/30/2023
只需使用 ,流具有重载运算符 bool 和 operator!。只需点击手册页面即可。if (file.getline(...istream
2赞 Remy Lebeau 8/30/2023
@Vladlen是的,在继续之前,您始终必须检查结果。此外,cplusplus.com 不是一个很好的参考站点,请改用 cppreference.com
0赞 Vladlen 8/31/2023
多亏了许多评论,我开始了解下一步行动。如果发生错误(除了),异常将被抛出并稍后在 中捕获。在我必须照顾之后.如果请求的缓冲区大小和实际读取的字节数相等(这只是一个工作案例,其他是错误),我调用并继续下一个 I/O,否则是 中的其他内部问题,并且我抛出异常以在相应的 .failbitreadline()catch()!file.readline()failbitclear()fstreamcatch()