c++ - 当我们尝试提取文件中的行,然后 istream::getline() 和 std::getline() 出现 `eof` 字符时,实际会发生什么
问题描述
roha.txt
I really love to spend time with you.
Let's go for coffee someday.
Enjoy whole day and cherish the memories.
代码 1
#include <iostream>
#include <fstream>
int main()
{
char str[100];
std::ifstream fin;
fin.open("roha.txt", std::ios::in);
for(int i=0; i<=3; i++)
{
std::cout<<bool(fin.getline(str,100) )<<" "<<str<<fin.fail()<<"\n";
}
}
输出
1 I really love to spend time with you.0
1 Let's go for coffee someday.0
1 Enjoy whole day and cherish the memories.0
0 1
代码 2
#include <iostream>
#include <fstream>
#include <string>
using std::string;
int main()
{
string str;
std::ifstream fin;
fin.open("roha.txt", std::ios::in);
for(int i=0; i<=3; i++)
{
std::cout<<bool(std::getline(fin,str) )<<" "<<str<<fin.fail()<<"\n";
}
}
输出
1 I really love to spend time with you.0
1 Let's go for coffee someday.0
1 Enjoy whole day and cherish the memories.0
0 Enjoy whole day and cherish the memories.1
我知道C-style char array
,istream::getline
和string
,std::getline
是不同的。但我想知道实际发生了什么。
我猜测对于string
, std::getline
,它会提取第 1 次、第 2 次的字符串,并且当它尝试提取它看到的第 3 次时,它eof
会在之前提取eof
。
下次我们尝试提取它时,只是遇到eof
所以它没有提取任何东西并设置fail-bit
。
string str
没有被修改,所以当我们尝试打印它时,只有最后提取的字符串被打印出来。
不知道我的想法对不对...
istream::getline()
我也不能就,提出任何此类情况C-style char array
。
解决方案
引用标准,第 21.3.3.4 节插入器和提取器 [string.io]:
第 6 条:
[…] 构造
sentry
对象后,如果sentry
转换为true
, 调用str.erase()
然后从中提取字符is
并将它们附加到str
[…] 直到发生以下任何情况:
- 文件结束出现在输入序列上(在这种情况下,
getline
函数调用is.setstate(ios_base::eofbit))
.- […]
第 29.7.4.1.3 节类basic_istream::sentry
:
explicit sentry(basic_istream<charT, traits>& is, bool noskipws = false);
效果: Ifis.good()
isfalse
, 调用is.setstate(failbit)
[…] If, 在任何准备完成后,is.good()
istrue
,ok_ != false
否则,ok_ == false
. 在准备期间,构造函数可能会调用setstate(failbit)
[…]
explicit operator bool() const;
回报:ok_
那么,字符串版本发生了什么:
- 您提取最后一个字符串。这会设置 eofbit,但不会设置 failbit
- 你又上线了
- getline 构造一个哨兵
- 哨兵检查
is.good()
。这是错误的,因为设置了 eofbit - 哨兵设置failbit并将其成员ok_设置为false
- getline 函数检查哨兵是否为真(运算符 bool)。这是假的
- getline 函数在清除旧字符串之前返回
第 29.7.4.3 节未格式化的输入函数
第 21 条(这是关于 C 字符串版本):
在任何情况下,如果
n
大于零,则将空字符(使用charT()
)存储到数组的下一个连续位置
其余的措辞与字符串版本类似。换句话说,getline 的 C 字符串版本总是存储一个'\0'
字符,即使它失败了。该std::string
版本没有,大概是因为如果您忘记检查故障位,它不会引入与 C 版本相同的内存安全问题。
推荐阅读
- llvm - 提取 PointerType 中包含的值
- json - id解码JSON上的keynotfound
- jquery - jQuery - 在多个条件下进行更改
- python - 将表从基于日期范围的列透视到日期索引
- pyspark - 简单示例上的 pandas udf showString 错误
- scala - 如何解决“/packages 不能表示为 URI”问题?
- python - 如何断言数据帧值为 NaN
- google-sheets - 输入日期序列号并保留目标格式
- arrays - 嵌套的 for 循环在 Flutter 中没有按预期工作
- android-studio - Android Studio 模拟器错误:INSTALL_FAILED_NO_MATCHING_ABIS