简体   繁体   English

std :: iostream是如何缓冲的?

[英]How is std::iostream buffered?

General use case 一般用例

I am trying to implement a basic shell. 我正在尝试实现一个基本的shell。

Description 描述

I need to read user input until some delimiters are pressed so a corresponding action can be performed. 我需要读取用户输入,直到按下某些分隔符,才能执行相应的操作。 Those delimiter could be a single 'a', a single 'b' or a single 'c'. 这些分隔符可以是单个“a”,单个“b”或单个“c”。

An input example would look like this (where > is the shell prompt): 输入示例如下所示(其中>是shell提示符):

> 111-222-333-444a
Ok, '111-222-333-444' entered

Why do I want inline delimiter instead of 'new-line' delimiter? 为什么我需要内联分隔符而不是“新行”分隔符?

Because I would like to listen to keyboard event such as 'up-arrow' to erase the current command and print the last command (implementing a history feature). 因为我想听一下键盘事件如'向上箭头'来删除当前命令并打印最后一个命令(实现历史记录功能)。

Because I would like to listen to keyboard event such as 'tabulation' to automaticly complete the current command (implementing auto-completion feature). 因为我想听'tabulation'这样的键盘事件来自动完成当前命令(实现自动完成功能)。

What I have so far 到目前为止我有什么

Up to now, my code looks like this: 到目前为止,我的代码如下所示:

bool done = false;
char c;
while (!done && std::cin.get(c))
{   
    switch (c)
    {
    case 'a':
        // Do something corresponding to 'a'
        done = true;
        break;

    case 'b':
        // Do something corresponding to 'b'
        done = true;
        break;

    case 'c':
        // Do something corresponding to 'c'
        done = true;
        break;

    default:
        // buffer input until a delimiter is pressed
        break;
    }
}

However, the loop seem to be executed only after the 'new-line' key have been pressed. 但是,循环似乎只有在按下“新行”键后才会执行。 This behaviour kill the interactive essence of the user input. 此行为会终止用户输入的交互式本质。

What is the question? 问题是什么?

I know std::ostream is buffered so content is not write to disk until some event occured but what about std::istream. 我知道std :: ostream是缓冲的,所以在发生某些事件之前内容不会写入磁盘,但是std :: istream呢。 Is it buffered? 它是缓冲的吗? If yes, how is it and what are my option to bypass this behaviour? 如果是,那么它是如何绕过这种行为的?

Also, I've tagged this question as 'homework' because, event if it is not a school exercice, it is an exercise I am trying to do by myself and I do not want to only pick a library that implement all this stuff. 此外,我已将此问题标记为“家庭作业”,因为如果不是学校的练习,这是我自己尝试做的练习,我不想只挑选一个实现所有这些东西的库。

If you are on a POSIX operating system, you can set the terminal to be unbuffered using the functions and structures declared in termios.h . 如果您使用的是POSIX操作系统,则可以使用termios.h声明的函数和结构将终端设置为无缓冲。 Basically you need to disable canonical input, and setup the terminal for non-canonical mode. 基本上你需要禁用规范输入,并为非规范模式设置终端。 These are some links that can help you with understanding the difference between the two terminal modes: 这些链接可以帮助您理解两种终端模式之间的区别:

  1. Noncanonical Input (from the libc manual) 非规范输入 (来自libc手册)

    In non-canonical input mode, the special editing characters such as ERASE and KILL are ignored. 非规范输入模式下,将忽略ERASE和KILL等特殊编辑字符。 The system facilities for the user to edit input are disabled in noncanonical mode, so that all input characters (unless they are special for signal or flow-control purposes) are passed to the application program exactly as typed. 用户编辑输入的系统工具在非规范模式下被禁用,因此所有输入字符(除非它们特别用于信号或流控制目的)都将完全按照键入的方式传递给应用程序。 It is up to the application program to give the user ways to edit the input, if appropriate. 应用程序可以根据需要为用户提供编辑输入的方法。

  2. Canonical vs. non-canonical terminal input 规范与非规范终端输入

    For canonical input - think shell; 对于规范输入 - 想想shell; actually, think good old-fashioned Bourne shell, since Bash and relatives have command-line editing. 实际上,想想老式的Bourne shell,因为Bash和亲戚都有命令行编辑。 You type a line of input; 你键入一行输入; if you make a mistake, you use the erase character (default is backspace, usually; sometimes DEL) to erase the previous character ... For non-canonical input - think vi or vim or ... you press a character, and it is immediately available to the program. 如果你犯了一个错误,你使用擦除字符(默认是退格,通常;有时是DEL)来擦除前一个字符...对于非规范输入 - 想想vi或vim或...你按一个字符,然后它可立即使用该程序。 You aren't held up until you hit return. 在你回归之前,你不会被耽搁。

  3. Description of Terminal Interface 终端接口说明

    This chapter describes a general terminal interface that is provided to control asynchronous communications ports. 本章介绍了为控制异步通信端口而提供的通用终端接口。 It is implementation-dependent whether it supports network connections or synchronous ports or both. 它是依赖于实现的,无论是支持网络连接还是同步端口,还是两者都支持

In essence though, the issue you're encountering is not with the C++ iostream interface itself, but rather has to-do with how the controlling terminal that the C++ iostream interface is reading from has been setup. 实质上,您遇到的问题不在于C ++ iostream接口本身,而在于如何设置C ++ iostream接口正在读取的控制终端。 Thus taking advantage of unbuffered I/O is going to be a platform-dependent operation, and will differ dependending on whether you're using Windows, or an actual POSIX-compliant platform (this includes POSIX-environments for Windows such as Cygwin). 因此,利用无缓冲的I / O将是一个依赖于平台的操作,并且将根据您使用的是Windows还是实际的POSIX兼容平台(这包括适用于Windows的POSIX环境,如Cygwin)而有所不同。

If you find that messing around with the terminal settings is too much of a problem, you can also look into a cross-platform curses programming library such as PDCurses that will abstract most of the complexities of the underlying terminal types. 如果你发现搞乱终端设置太麻烦了,你也可以查看一个跨平台的curses编程库,比如PDCurses ,它将抽象出底层终端类型的大部分复杂性。

The answers to the immediate questions on whether and how std::istream is buffered are: yes, std::istream is buffer using a class derived from std::streambuf which defines the actual buffering and reading approach for a concrete source (or, when using an std::ostream for a destination). 关于是否以及如何缓冲std::istream的直接问题的答案是:是的, std::istream是使用从std::streambuf派生的类的缓冲区,它定义了具体源的实际缓冲和读取方法(或者,当使用std::ostream作为目的地时)。 Whether this really does any buffering depends on this concrete class and its operation can generally not avoided. 这是否真的有任何缓冲取决于这个具体的类,它的操作通常无法避免。

That said , this isn't you problem! 那就是说,这不是你的问题! The problem is that normally input isn't sent to standard input if a program until the newline key is hit. 问题是,如果直到新行键被击中,程序通常不会将输入发送到标准输入。 This is so that some line editing can be done by the terminal implementation and doesn't have to be done by every program. 这样一些行编辑可以由终端实现完成,而不必由每个程序完成。 Unfortunately, there is no portable approach to change this. 不幸的是,没有可移植的方法来改变这种情况。 On POSIX youcan turn the standard input stream (using file descriptor 0 ) into non-canonical mode using tcgetattr() and tcsetattr() . 在POSIX上,您可以使用tcgetattr()tcsetattr()将标准输入流(使用文件描述符0 )转换为非规范模式。 I don't know how to achieve this on non-POSIX systems. 我不知道如何在非POSIX系统上实现这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM