如何在 C++?... 下推广“clearerr()”

How would one generalise `clearerr()` under C++?…

提问人:mavenor 提问时间:9/26/2021 最后编辑:mavenor 更新时间:10/7/2021 访问量:235

问:

TL的;博士

我知道,如果一个程序侦听 EOF(例如)作为停止接受输入的符号,例如依靠条件,如 while (std::cin) {...},则需要调用 cin.clear() 才能再次读取标准输入(想知道更多信息的读者,请参阅此表)。^D

我最近了解到这是不够的,并且底层 C 文件描述符(包括 stdin)需要运行 clearerr() 才能忘记 EOF 状态

由于需要一个 C 样式的文件描述符,并且 C++ 主要使用 s 等操作(例如),我想通用一些代码(见下文)以在任何相关的 C 样式文件描述符上运行,即使它可能不是。clearerr()std::basic_streambufcinclearerr()streambufstdin

编辑 (1&2):
我想知道 stdin 是否是唯一行为如下的文件描述符(需要 clearerr() 才能运行)......?
如果不是,那么下面的代码应该结束泛化问题(zkoza 在他们的回答中指出的想法)

正如 zkoza 在下面的评论中指出的那样,从逻辑上讲,是唯一需要这种处理的文件描述符(即)。只需检查给定的 C++ 流是否真的附加到:
stdinclearerr()*std::cin.rdbuf()

std::istream theStream /* some stream with some underlying streambuf */

if (theStream.rdbuf() == std::cin.rdbuf())
    clearerr(stdin);

背景

我正在用 C++ 编写一个工具,我需要两次获取多行用户输入。

我知道有多种方法可以获取多行输入(例如等待双换行符),但我想使用 EOF 作为用户的信号,表明他们已经完成了——就像你或 .gpg -s-e

经过多次咨询(这里这里cppreference.com),我决定使用......(我引用第三个):

惯用的C++输入循环,例如 [...]

while(std::getline(stream, string)){...}

由于它们依赖于 std::basic_ios::operator bool 来完成它们的工作,因此我确保在第一个和第二个用户输入指令之间清除 cin.rdstate()(使用 )。cin.clear()

我的代码要点如下:

std::istream& getlines (std::basic_istream<char> &theStream,
                        std::vector<std::string> &stack) {
    std::ios::iostate current_mask (theStream.exceptions());
    theStream.exceptions(std::ios::badbit);
    std::string &_temp (*new std::string);
    while (theStream) {
        if (std::getline(theStream, _temp))
            stack.push_back(_temp);   // I'd really like the input broken...
                                      // ... into a stack of `\n`-terminated...
                                      // ... strings each time
    }

    // If `eofbit` is set, clear it
    // ... since std::basic_istream::operator bool needs `goodbit`
    if (theStream.eof())
        theStream.clear(theStream.rdstate()
                        & (std::ios::failbit | std::ios::badbit));
                        // Here the logical AND with
                        // ... (failbit OR badbit) unsets eofbit
    
    // std::getline sets failbit if nothing was extracted
    if (theStream.fail() && !stack.size()) {
        throw std::ios::failure("No input recieved!");
    }
    else if (theStream.fail() && stack.size()) {
        theStream.clear(theStream.rdstate() & std::ios::badbit);
        clearerr(stdin); // 👈 the part which I want to generalise
    }
    
    delete &_temp;
    theStream.exceptions(current_mask);
    return theStream;
}
C++ IO 缓冲区 EOF 文件描述符

评论

0赞 Adrian Mole 9/26/2021
我有点困惑。据我了解,其效果与.cin.clear()clearerr(stdin)
0赞 mavenor 9/26/2021
这也是我所想的(也是希望的)!但显然不是:/感谢您的回复!

答:

1赞 zkoza 10/4/2021 #1

这将满足您的需求:

#include <iostream>

int main()
{
    std::cin.sync_with_stdio(true);
    char c = '1', d = '1';
    std::cout << "Enter a char: \n";
    std::cin >> c;
    std::cout << (int)c << "\n";
    std::cout << std::cin.eof() << "\n";
    std::cin.clear();
    clearerr(stdin);
    std::cout << std::cin.eof() << "\n";

    std::cout << "Enter another char: \n";
    std::cin >> d;
    std::cout << (int)d << "\n";
    std::cout << std::cin.eof() << "\n";
}

它之所以有效,是因为默认情况下,C++ 与 C 绑定(因此,实际上不需要第一行)。您必须修改代码以检查流是否是,如果是,请执行std::cinstdinstd::cinclearerr(stdin);

编辑:

实际上,仅确保 C 和 C++ 接口之间的同步,但在内部,它们在相同的文件描述符上工作,这可能就是无论接口是否绑定的原因sync_with_stdioclearerr(stdin);sync_with_stdio

编辑2:这些能回答你的问题吗?从 std::fstream https://www.ginac.de/~kreckel/fileno/ 获取 FILE*

评论

0赞 mavenor 10/6/2021
你好!首先,感谢您的回复!正如你自己指出的那样,我继续检查传递给我的职能是否确实第一位的。我通过测试 .std::istream&std::cintheStream.rdbuf() == std::cin.rdbuf()
0赞 mavenor 10/6/2021
刚刚意识到我原来的问题是多么可怕,所以我要去整理一下。主要目标实际上是找到一种泛化 clearerr() 的方法。现在我怀疑一个人会遇到另一个,而不是与那个同步的人需要......仍然喜欢概括 😅streambufstdinclearerr()
0赞 mavenor 10/6/2021
所以总而言之,如果不是遇到EOF后可能需要的唯一文件描述符,我该如何对任何给定的文件描述符进行概括?(不幸的是,它不能翻译成类似 C 的文件描述符)stdinclearerr()clearerr()std::streambuf
1赞 zkoza 10/6/2021
为什么任何其他流都应该受到关注?因为有一个“中间人”:处理用户输入的终端。如果连接到 Linux 终端,它永远不会接收对应于 、 、 、 的字节。您只能通过检查 eof 和 fail 标志(它们都是由 ctrl+D 设置)来判断它被按下的。为什么 c++ 编译器(至少是 gcc 和 clang)不将流的状态与底层 C 库定义的状态相关联,这对我来说是一个真正的谜。但他们肯定是故意这样做的。std::cincinctrl+Cctrl+Zctrl+Uctrl+Wctrl+Dctrl+D
0赞 mavenor 10/7/2021
关于成为 MITM,你有一个非常好的观点!你是对的,我想这是真的,这是唯一需要特殊处理的文件描述符。谢谢,zkozastdin