理解阅读+;用c写

理解阅读+;用c写,c,stdin,read-write,C,Stdin,Read Write,关于这个片段,我想澄清一些事情。我们运行它,./exec\u文件,比如说我们只需按Enter键。我们移动到下一行,读取1字节的'\n',然后将其写入标准输出,再往下一行。。。很简单。现在假设我们键入h,然后输入。程序在下一行吐出h,其中包含一个不可见的'\n' 在我们键入h后查看代码,它会将代码读入缓冲区,然后将其写入标准输出,但不知何故,程序会等待在下一行吐出代码,直到我按下Enter键。如何 最后,当我们第一次点击while循环时,最初不会读取并返回0,因为我们最初没有键入任何内容???st

关于这个片段,我想澄清一些事情。我们运行它,./exec\u文件,比如说我们只需按Enter键。我们移动到下一行,读取1字节的
'\n'
,然后将其写入标准输出,再往下一行。。。很简单。现在假设我们键入
h
,然后输入。程序在下一行吐出
h
,其中包含一个不可见的
'\n'

在我们键入
h
后查看代码,它会将代码读入缓冲区,然后将其写入标准输出,但不知何故,程序会等待在下一行吐出代码,直到我按下Enter键。如何


最后,当我们第一次点击while循环时,最初不会读取并返回
0
,因为我们最初没有键入任何内容???

stdin
的行为与大多数其他流稍有不同

首先,输入是行缓冲的。这意味着在按enter键之前,输入不可用。这解释了当按下enter键后,
h
才会显示

因为它是一条小溪,所以它并没有真正的终点。当没有数据可读取时,调用将阻塞,直到有数据可用(或直到程序收到信号),而不是失败。插座也是这样工作的

使用
fcntl
的阻塞行为:

char buf[1];
if (argc == 1) {
    while (read(STDIN_FILENO, buf, 1) > 0) {
        write(1, buf, sizeof(buf));
    }
}

stdin
的行为与大多数其他流稍有不同

首先,输入是行缓冲的。这意味着在按enter键之前,输入不可用。这解释了当按下enter键后,
h
才会显示

因为它是一条小溪,所以它并没有真正的终点。当没有数据可读取时,调用将阻塞,直到有数据可用(或直到程序收到信号),而不是失败。插座也是这样工作的

使用
fcntl
的阻塞行为:

char buf[1];
if (argc == 1) {
    while (read(STDIN_FILENO, buf, 1) > 0) {
        write(1, buf, sizeof(buf));
    }
}

如果您仍希望阻塞发生,但希望逐个字符读取,则可以使用
termios
配置如何将输入提供给程序。请参阅下面的代码

#包括
#包括
#包括
#包括
int main()
{
char-buf[1];
结构术语,术语来源;
if(tcgetattr(0,和term_orig)){
printf(“tcgetattr失败\n”);
出口(-1);
}
术语=术语来源;
术语c_lflag&=~ICANON;
术语c|lflag |=回声;
术语c_cc[VMIN]=1;
term.c_cc[VTIME]=0;
if(tcsettr(0、TCSANOW和term)){
printf(“tcsetattr失败\n”);
出口(-1);
}
while(读(0,buf,1)>0){
写入(1,buf,sizeof(buf));
}
返回0;
}

如果您仍希望阻塞发生,但希望逐个字符读取,则可以使用
termios
配置如何将输入提供给程序。请参阅下面的代码

#包括
#包括
#包括
#包括
int main()
{
char-buf[1];
结构术语,术语来源;
if(tcgetattr(0,和term_orig)){
printf(“tcgetattr失败\n”);
出口(-1);
}
术语=术语来源;
术语c_lflag&=~ICANON;
术语c|lflag |=回声;
术语c_cc[VMIN]=1;
term.c_cc[VTIME]=0;
if(tcsettr(0、TCSANOW和term)){
printf(“tcsetattr失败\n”);
出口(-1);
}
while(读(0,buf,1)>0){
写入(1,buf,sizeof(buf));
}
返回0;
}

终端默认为行缓冲,因为它处于规范模式。从Linux手册
tcgetattr(3)

规范模式与非规范模式 关于佳能的设置 c_lflag中的标志确定终端是否在 规范模式(ICANON set)或非规范模式(ICANON unset)。 默认情况下,ICANNON已设置

在规范模式下:

  • 逐行提供输入。输入行是 可获得的 键入其中一个行分隔符时(NL、EOL、EOL2;或EOF at 行的开始)。除EOF外,行分隔符 包含在读取(2)返回的缓冲区中

  • 行编辑已启用(擦除、终止;如果IEXTEN标志为 设置: WERASE,重印,下一页)。read(2)最多返回一行 输入;如果读取(2)请求的字节数少于中的可用字节数 输入的当前行,则只需要请求的字节数 读取,其余字符将在将来可用 读(2)

通过使用适当的标志调用
tcgetattr
,可以关闭终端上的规范模式。首先禁用规范模式;然后将超时设置为0;对于阻塞读取,将最小读取设置为1;对于非阻塞读取,将最小读取设置为0。通常也会禁用本地echo,否则键入的所有内容仍将自动可见(并在程序中显示两次):

#包括
#包括
#包括
int main(){
结构termios旧设置、新设置;
int是_终端;
//检查stdin是否是一个要开始的终端
如果(is_terminal=isatty(STDIN_FILENO)){
//获取旧设置
tcgetattr(标准文件号和旧设置);
新设置=旧设置;
//禁用规范模式和回显
新设置.c\u lflag&=(~ICANON&~ECHO);
//在读取返回之前,必须至少写入一个字符
新建_设置。c_cc[VMIN]=1;
//没有超时
新建设置。c_cc[VTIME]=0;
tcSetTTR(标准文件号、TCSANOW和新设置);
}
而(读取(标准文件号,buf,1)>0){
//将此添加到此处,以便您可以逐个字符验证它,
//而不是来自终端的本地回声
写入(标准输出文件号“>”,1);
写入(STDOUT_FILENO、buf、sizeof(buf));
}
//如果是终端,最后恢复旧设置
if(is_终端){
tcSetTTR(标准输入文件号、TCSANOW和旧设置);
}
返回0;
}

终端默认为行缓冲,因为它是
#include <stdio.h>
#include <unistd.h>
#include <termios.h>

int main() {
    struct termios old_settings, new_settings;
    int is_terminal;

    // check whether the stdin is a terminal to begin with
    if (is_terminal = isatty(STDIN_FILENO)) {     
        // get the old settings
        tcgetattr(STDIN_FILENO, &old_settings);

        new_settings = old_settings;

        // disable canonical mode and echo
        new_settings.c_lflag &= (~ICANON & ~ECHO);

        // at least one character must be written before read returns
        new_settings.c_cc[VMIN] = 1;

        // no timeout
        new_settings.c_cc[VTIME] = 0;

        tcsetattr(STDIN_FILENO, TCSANOW, &new_settings);
    }

    while (read(STDIN_FILENO, buf, 1) > 0) {
        // add this here so that you can verify that it is character by character,
        // and not the local echo from the terminal
        write(STDOUT_FILENO, ">", 1);
        write(STDOUT_FILENO, buf, sizeof(buf));
    }

    // finally restore the old settings if it was a terminal
    if (is_terminal) {
        tcsetattr(STDIN_FILENO, TCSANOW, &old_settings);
    }
    return 0;
}