VS C++ 初学者日记(2)输入流缓存清空

时间:2022-06-16 04:01:46

输入流缓存不清空会导致的问题

在这个系列的上一篇中(已经过去好久了哈),经过研究写了一个驻屏的段落。当时其时还留有一些尾巴,今天在这里探讨一下。


首先回顾一下上次的驻屏程序:写成一个staycreen函数:
stayscreen.h

// stayscreen.h -- stayscreen programming head file
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;
using std::getline;
using std::get;
using std::endl;

void stayscreen();

stayscreen.cpp

// stayscreen.cpp -- stayscreen cpp file
#include "stayscreen.h"

void stayscreen()
{
    string exitflag("\n");
    cout << "Press Q or q key to quit...\n";
    getline(cin, exitflag);
    while (exitflag.find('q') == string::npos && exitflag.find('Q') == string::npos)
    {
        cout << "Press Q or q key to quit...\n";
        getline(cin, exitflag);
    }
}

调用格式大体上为

blablabla
#include "stayscreen.h"
blablabla

int main()
{
   blablabla
   stayscreen();
   return 0;
}

大多数情况下这个程序运转是正常的,偶尔会出现问题,这也是为什么说“留有尾巴”的原因。那么会出现什么问题呢?
你可能会注意到,在stayscreen.cpp里有一些奇怪的地方:用于读取输入的

    cout << "Press Q or q key to quit...\n";
    getline(cin, exitflag);

在后面的while循环里明明已经写过一次了。为什么又在循环外补了一个?
这在当时其实是无奈之举,因为在读入输入的时候,有时候会读到前面main程序输入输出流尾巴上的回车符,从而导致判断exitflag是否为“Q/q”的程序段落始终读到的是一个回车符,从而陷入死循环,现象就是不停地输出”Press Q or q key to quit…”
为了解决这个问题,只好在进入while循环之前先读取一次,目的是如果读到了缓冲区里的回车符,可以通过循环外的这一次读取洗掉。可是并不是每段程序都能适应(尤其是缓冲区里可能有一个以上回车符的时候)。这个问题一直没有得到很好的解决。


当然了,作为初学者,阶段性的问题解决不好是很正常的事情。这两天刚好在学习输入和输出部分相关的知识。看到cin.clear()和cin.ignore()的用法时,突然豁然开朗。在最后的读取判断前,为了防止前置程序的干扰,最好的办法可不就是把缓冲区的内容清空吗?
在c语言风格里,用的是fflush(stdin)这一句,但是在c++里,用的是

cin.clear();
cin.ignore();

于是stayscreen.cpp改成

// stayscreen.cpp -- stayscreen cpp file
#include "stayscreen.h"

void stayscreen()
{
    string exitflag("\n");
    cin.clear();
    cin.ignore();
    while (exitflag.find('q') == string::npos && exitflag.find('Q') == string::npos)
    {
        cout << "Press Q or q key to quit...\n";
        getline(cin, exitflag);
    }
}

这样就不会发生逻辑上明明没问题,却总是因为缓冲区内回车符的影响,导致的读取错位了。
特别值得注意的是,cin.clear()会导致下一个cin流不读取输入,所以需要用cin.ignore()处理一次,这两句共同使用就相当于是一次完整的流清空和重置了。