我知道 C++ 流函数是构建在 C 的
stdio
库之上的。
我必须在 C 中做什么才能得到与
cin.ignore(n)
相同的结果?stdio
函数 fseek(stdin, n, 0)
还是 cin.ignore
正在使用其他方法?
不,没有。但让我们看看幕后发生的事情
cin.ignore()
。让我们看 llvm libcxx 源代码,我发现它们比 gcc 的浏览速度更快。
extern istream cin; 位于 iostream 中,但它是在应用程序启动时在 iostream.cpp 中使用静态分配的缓冲区和从旧的“旧”构建的 __stdoutbuf 对象进行初始化的
FILE *
stdin
:
_ALIGNAS_TYPE (istream) char cin [sizeof(istream)];
ios_base::Init::Init() {
istream* cin_ptr = ::new(cin) istream(::new(__cin) __stdinbuf <char>(stdin) );
...
istream::ignore()
功能可以在istraem中找到。这非常简单,首先我们检查用户是否想要清除流中的所有字符或仅清除其中的一些字符(if (__n == numeric_limits<streamsize>::max())
)。然后,该函数在循环中调用 this->rdbuf()->sbumpc()
预定义的计数量(或无限循环,如果 __n
等于 numeric_limits<steramsize::max()
)。我们可以从 cppreference中发现
sbumpc()
是 std::basic_streambuf
的成员:
int_type sbumpc();
Reads one character and advances the input sequence by one character.
If the input sequence read position is not available, returns uflow(). Otherwise returns Traits::to_int_type(*gptr()).
因此我们可以简单地推断出
this->rdbuf()
返回__stdinbuf<char>(stdin)
的句柄。在 cin::ignore
函数中,对 __stdinbuf<char>(stdin)::sbumpc()
的调用被多次调用,我们想忽略多少个字符。那么我们就去sbumpc()
吧!首先我们来看看streambuf:
int_type sbumpc() {
if (__ninp_ == __einp_)
return uflow();
return traits_type::to_int_type(*__ninp_++);
}
因此
if (__ninp_ == __einp_)
正在 streambuf
对象中进行一些内部缓冲,如果缓冲区中已经有缓冲的字符,则不会调用 uflow()
。 __ninp__
指针在每次读取后都会递增,这一定是这样。 uflow()
被 __stdinbuf : public basic_streambuf< .... >
重载,来自 __std_stream:
template <class _CharT>
typename __stdinbuf<_CharT>::int_type
__stdinbuf<_CharT>::uflow()
{
return __getchar(true);
}
噗,我们去
__getchar
看看true
参数是什么。它位于 __std_stream 的正下方。template <class _CharT>
typename __stdinbuf<_CharT>::int_type
__stdinbuf<_CharT>::__getchar(bool __consume) {
....
int __c = getc(__file_);
if (__c == EOF)
return traits_type::eof();
...
}
让我们从头开始吧:
cin
是一个 istraem
对象,并从 __stdinbuf<char>(stdin)
istream::ignore()
调用 basic_streambuf::sbumpc()
预定义的次数,可能是在使用 stdin
basic_streambuf::sbumpc()
负责一些缓冲,如果缓冲区为空,则调用 basic_streambuf::uflow()
。basic_streambuf::uflow()
被 __stdinbuf::uflos()
过载并调用 __stdinbuf::__getchar()
__sinbuf::__getchar()
调用 getc(__file__)
,因此可能 getc(stdin)
从流中读取一个字符总结一下:
void stdin_ignore(size_t n, int delim)
{
while (n--) {
const int c = getc(stdin);
if (c == EOF)
break;
if (delim != EOF && delim == c) {
break;
}
}
另外
scanf("%*[^\n]\n"); // Ignores a line
或
scanf("%*s"); // Ignores one string
我知道这个问题发布已经有一段时间了,但这个解决方案似乎可以在 Linux、Mac OS 和 Windows(包括 MSVC)之间移植。如果您试图让用户输入数字,而他们不小心输入了 123a45 之类的内容,则此后的任何数字或字符串/字符输入请求都将被跳过,因为“a45”仍将位于标准输入“缓冲区”中。要解决此问题,请查看以下示例:
char someCString[64];
const int LENGTHOFCSTRING = 64;
fgets(someCString, LENGTHOFCSTRING, stdin);
这将从 stdin 读取所有数据(最多 64 个字符),包括 ' ',并将其放入 someCString 数组中。如前所述,您还可以创建一个函数 (stdin_ignore),它一次执行一个字符,对 C 字符串的长度没有限制。