如何在VS中清空cin缓冲区(C++)

问题代码

学习文件读写时,写了下面这段demo

#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//以文本文件方式写入
void test01()
{
    cout << "<<写入文件>>" << endl;
    int n;
    cout << "要输入的行数:";
    cin >> n;
    ofstream ofs;
    ofs.open("test.txt", ios::out);   //创建test.txtA
    string buf;
    for (int i = 1; i <= n; i++)
    {
        cout << i << ":"; //打印当前所在行号
        getline(cin, buf);  //输入内容
        ofs << buf <<endl;  //写入文件
    }
    ofs.close();
}
//以文本文件方式读取
void test02()
{
    cout << "\n<<读取文件>>" << endl;
    ifstream ifs;
    ifs.open("test.txt", ios::in);
    if (!ifs.is_open())
    {
        cout << "文件打开失败!" << endl;
    }
    string buf;
    int i = 1;
    while (getline(ifs, buf))
    {
        cout << i++ << ":";
        cout << buf << endl;
    }
    ifs.close();
}
int main()
{   
    test01();
    test02();
    system("pause");
    return 0;
}

代码本身没有语法问题,但运行结果如下:

file

当输入行号后,第一行自动读入了空内容,直接跳转到第二行

问题排查

首先想到的就是第一次循环自动读入了一个换行符,因为在实际输入内容之前,要先输入行数然后回车。
验证的办法也很简单,把指定行数改成固定行数,去掉实际内容前的行数输入,也即是把test01()函数改成如下:

void test01()
{
    cout << "<<写入文件>>" << endl;
    ofstream ofs;
    ofs.open("test.txt", ios::out);
    string buf;
    for (int i = 1; i <= 2; i++) //直接指定2行
    {
        cout << i << ":"; //打印当前所在行号
        getline(cin, buf);  //输入内容
        ofs << buf <<endl;  //写入文件
    }
    ofs.close();
}

此时运行结果如下:

file

此时第一行可以正常输入,显然问题就出在getline()之前的那次cin输入
此外,将getline(cin,buf)换用cin>>buf实现,也可以正常输入(只是cin无法正常读取空格)

问题解决

在确定问题后,就去百度了cin的详解,在CSDN的一篇文章中找到了如下解释:

程序的输入都有一个缓冲区,即输入缓冲区。一次输入过程是这样的,当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin对象直接从输入缓冲区中取数据。正因为cin对象是直接从缓冲区取数据的,所以有时候当缓冲区中有残留数据时,cin对象会直接取得这些残留数据而不会请求键盘输入。
当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。但是如果读取成功,字符后面的分隔符是残留在缓冲区的,cin>>不做处理。
但是,getline()读取数据时,并非像cin>>那样忽略第一个换行符,getline()发现cin的缓冲区中有一个残留的换行符,不阻塞请求键盘输入,直接读取,送入目标字符串后,再将换行符替换为空字符’\0’。

那么问题就很好解决了,既然已经知道了getline()会直接读取cin缓冲区中的内容,接下来要做的就是在getline()被调用之前清空cin缓冲区

清空cin缓冲区

网上比较广泛的说法有如下几个:

  • cin.sync();
  • fflush(stdin);
  • cin.ignore(INT_MAX, ‘\n’);

但经过实测,前两种方法均无法在vs2019中生效,因此建议使用第三种方法,将test01()改成如下:

void test01()
{
    cout << "<<写入文件>>" << endl;
    int n;
    cout << "要输入的行数:";
    cin >> n;
    cin.ignore(INT_MAX, '\n');        //清空cin缓存
    ofstream ofs;
    ofs.open("test.txt", ios::out);   //创建test.txt
    string buf;
    for (int i = 1; i <= 2; i++)
    {
        cout << i << ":"; //打印当前所在行号
        getline(cin, buf);  //输入内容
        ofs << buf <<endl;  //写入文件
    }
    ofs.close();
}

再次运行结果一切正常:

file

cin.ignore(INT_MAX, '\n');的含义是:当遇到换行符时,清空缓冲区内所有内容(换行符也被清除),其中INT_MAX是C++中的宏常量,意为int最大值,也可以用std::numeric_limits< streamsize >::max()代替,意为IO流最大字节数

ignore的函数原型为:istream & ignore(int n =1, int delim = EOF); 为方便理解,也可以写成:cin.ignore(count, c); 其中c代表字符,count代表提取的字符数,当遇到以下三种情况时,清空缓冲区内容:

  1. 提取的字节数达到count数量
  2. 遇到EOF终结符
  3. 遇到指定的c字符(c字符也被提取一并清空)

参考文章1:https://blog.csdn.net/selina8921/article/details/79067941
参考文章2:https://blog.csdn.net/weixin_35806027/article/details/112994305

本文作者:小小黑
本文链接:https://lonelyenderman.top/archives/848
版权声明:本站采用 BY-NC-SA 进行许可。转载请注明出处!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(*^▽^*)
 ̄﹃ ̄
(╯‵□′)╯︵┴─┴
(~ ̄▽ ̄)~
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
( ͡° ͜ʖ ͡°)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
つ﹏⊂
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
(´▽`ʃ♡ƪ)
w(゚Д゚)w
(๑•̀ㅂ•́)و✧
(#`O′)
凸(艹皿艹 )
o(≧口≦)o
≡ω≡
(*/ω\*)
○| ̄|_
(⊙ˍ⊙)
Σ(っ °Д °;)っ
o( ̄ヘ ̄o#)
<( ̄︶ ̄)>
(。・∀・)ノ゙
(o゜▽゜)o☆
╥﹏╥
ヾ(´・ω・`)ノ
😂
😀
😅
😊
🙂
😍
😘
😜
😝
😏
😒
🙄
😳
😔
😫
😱
😭
😶
🌚
😣
🤨
😣
🤐
😪
🤤
🥵
🤮
😨
😱
😓
🤬
👴
🤡
🙈
💊
🙏
🤺
💩
👻
🙌
🖕
👍
👫
👌
🙏
👀
🐒
🔪
Source: github.com/zhheo/Sticker-Heo
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
Heo
花!
上一篇
下一篇