首页 > 解决方案 > 当我们尝试提取文件中的行,然后 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::getlinestring,std::getline是不同的。但我想知道实际发生了什么。

我猜测对于string, std::getline,它会提取第 1 次、第 2 次的字符串,并且当它尝试提取它看到的第 3 次时,它eof会在之前提取eof

下次我们尝试提取它时,只是遇到eof所以它没有提取任何东西并设置fail-bit

string str没有被修改,所以当我们尝试打印它时,只有最后提取的字符串被打印出来。

不知道我的想法对不对...

istream::getline()我也不能就,提出任何此类情况C-style char array

标签: c++stringfile-handlingeofgetline

解决方案


引用标准,第 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); 效果: If is.good()is false, 调用is.setstate(failbit) […] If, 在任何准备完成后, is.good()is true,ok_ != false否则, ok_ == false. 在准备期间,构造函数可能会调用setstate(failbit) […]

explicit operator bool() const; 回报:ok_

那么,字符串版本发生了什么:

  1. 您提取最后一个字符串。这会设置 eofbit,但不会设置 failbit
  2. 你又上线了
  3. getline 构造一个哨兵
  4. 哨兵检查is.good()。这是错误的,因为设置了 eofbit
  5. 哨兵设置failbit并将其成员ok_设置为false
  6. getline 函数检查哨兵是否为真(运算符 bool)。这是假的
  7. getline 函数在清除旧字符串之前返回

第 29.7.4.3 节未格式化的输入函数

第 21 条(这是关于 C 字符串版本):

在任何情况下,如果n大于零,则将空字符(使用charT())存储到数组的下一个连续位置

其余的措辞与字符串版本类似。换句话说,getline 的 C 字符串版本总是存储一个'\0'字符,即使它失败了。该std::string版本没有,大概是因为如果您忘记检查故障位,它不会引入与 C 版本相同的内存安全问题。


推荐阅读