直到 CTRL + D 的输入间隔

Intervals on input until CTRL + D

提问人:Pavel Straka 提问时间:11/9/2017 最后编辑:Petr SkocikPavel Straka 更新时间:11/11/2017 访问量:999

问:

我有写成我阅读的间隔,但问题是我需要在许多这样的间隔内使用我的函数,直到我在 Unix 上按下 或。<a;b>scanf("<%d;%d>", &a, &b);CTRL + ZCTRL + D

到目前为止,我尝试过这样的东西,但它不起作用。

int main( void ) {
int a=0, b=0;

printf("Intervals:\n");
while(scanf("<%d;%d>", &a, &b) == 2 && !feof(stdin)){

    printf("Distinct rectangular cuboids: %d\n", capacities(a, b));

}
return 0;
c scanf stdin eof feof

评论

2赞 BLUEPIXY 11/9/2017
尝试while(scanf(" <%d;%d>", &a, &b) == 2){
0赞 Pavel Straka 11/9/2017
它可以工作,但它希望按两次才能终止CTRL + D
0赞 BLUEPIXY 11/9/2017
所以试试吧while(scanf("<%d;%d>%*c", &a, &b) == 2){
3赞 chux - Reinstate Monica 11/9/2017
如果为 true,则始终为 true。scanf("<%d;%d>", &a, &b) == 2!feof(stdin)
0赞 chux - Reinstate Monica 11/9/2017
scanf("<%d;%d>", &a, &b)将尾随留在 中。读一行,然后'\n'stdinfgets()sscanf(buffer, "<%d;%d>", &a, &b)

答:

2赞 kabanus 11/9/2017 #1

你的问题不简明扼要。这:

int main( void ) { 
    int a=0, b=0;

    printf("Intervaly: \n");
    while(scanf("<%d;%d>",&a,&b) == 2){ 
        printf("Ruznych kvadru: %d\n", capacity(a, b));
    }   
    return 0;  
}

实际上做你想要的,无论是长线还是一行一行 - 两者都可以工作。您的问题隐藏在评论中,EOF 不会立即终止它 - 而是您需要按 CTRL-D 两次才能停止它。原因也在评论中。<%d;%d>

一旦你以你的方式扫描,你将在输入流中留下一个(因为你必须按回车键才能在不终止的情况下发送你的输入)。有两种方法可以解决这个问题 - 将其添加到 中,它不太灵活并且存在一些问题,或者在 while 中清除它:\nscanf

while(scanf("<%d;%d>",&a,&b) == 2){ 
    printf("Ruznych kvadru: %d\n", capacity(a, b));
    getchar();
}   

您会发现现在您需要按一次 CTRL-D。如果您尝试从文件进行输入重定向,您会看到此问题也不存在。为什么会这样?您在输入流中。您按 EOF,因此 scanf 会获得一个换行符并忽略它 - 等待新输入。只有现在,新的 EOF 才会杀死 .\nscanf

我建议在这个网站上搜索问题,看看为什么不使用它,以及其他不错的选择。scanf

补遗

评论中给出的更优雅的解决方案,@BluePixy向我指出,@DavidBowling向我指出,用于您的:scanf

scanf("<%d;%d>%*c", &a, &b);

这自然会吞噬额外的字符,包括新空间,而不需要额外的.我认为这是这种用法最优雅的解决方案。getcharscanf

在 OP 和 David 的评论之后(再次感谢!)为了考虑 0 个或更多空格,您只需要格式中的空格指令:

scanf("< %d ; %d >%*c", &a, &b);

现在,可以在每个整数周围添加空格(也可能不会)。

评论

0赞 ad absurdum 11/9/2017
“将它添加到 中,这不太灵活并且有一些问题”——您如何想象将其包含在对 的调用中,有什么问题?scanfscanf()
0赞 kabanus 11/9/2017
@DavidBowling - 显然我现在不能在一行中输入多个坐标。它将读取第一个,并且缓冲区将与其余输入保持满,这将导致其他烦恼。我宁愿它不出现在答案中。scanf("<%d;%d>\n",&a,&b)
0赞 ad absurdum 11/9/2017
无论如何,在格式字符串中使用尾随空格是一个错误;这会导致继续匹配空格字符,直到遇到非空格字符,从而导致交互性问题。但是:scanf(“<%d;%d>%*c”, &a, &b)呢?在这里,在一行上可以有多个输入(只要它们之间只有一个字符;您的解决方案需要相同的字符)。scanf()scanf()
0赞 kabanus 11/9/2017
@DavidBowling我同意第一部分,没有考虑第二部分——这是一个好主意,我没有考虑过。您想将其添加为您自己的答案还是将其添加到此答案中?我认为如果我们坚持使用 ,它比我的更好,因为它自然会吞噬换行符。唯一的缺点是它不会因坏角色而中断 - 因此这必须由 OP 需要什么来决定。scanf
0赞 kabanus 11/9/2017
我把它收回来,它有一切优势,想到这一点值得称赞。
0赞 Petr Skocik 11/9/2017 #2

至于循环条件,应该就足够了,但你可能还应该跟踪数字以进行错误诊断:while( scanf(" <%d;%d>", &a, &b)) == 2 )

int nread=2;
while( (nread=scanf(" <%d;%d>", &a, &b)) == 2){ //...

这部分更复杂。 导致规范设置的终端驱动程序生成信号。Ctrl-ZCtrl-ZSIGTSTP

此信号的默认处理是将进程置于睡眠状态,这意味着您 不能简单地使用 ISO C 的函数来注册信号处理程序,因为 如果出现以下情况,这些信号处理程序可能会无意中恢复原始信号处置 接收到多个信号实例,这将使您的进程停止。signal

您需要进入 POSIX 并使用 .sigaction

然后,处理程序可以设置一个易失性标志,这将是终止循环的信号。 由于在内部接收信号会导致失败,因此对此的鲁棒错误处理有点复杂:sigactionscanfscanfEINTR

#include <stdio.h>
#include <signal.h>
#include <errno.h>

static int volatile ctrl_z;
void SignalHandler( int Signum )
{
    (void)Signum;
    ctrl_z=1;
}

int main( void ) {
    // ctrl + z
    sigaction(SIGTSTP, &(struct sigaction){ .sa_handler=SignalHandler }, 0);

    int a=0, b=0, nread=2;

    /*...*/
    while( (nread=scanf(" <%d;%d>", &a, &b)) == 2){

       /*...*/

        if(ctrl_z)
            return 0;
        errno=0; /*so we know if scanf failed because of the signal*/
    }
    if ( (!ferror(stdin) && feof(stdin)) ||
           (ferror(stdin) && errno==EINTR && ctrl_z) )
        return 0;
    return 1;
}