【问题标题】:Get scanf to quit when it reads a newline?让scanf在读取换行符时退出?
【发布时间】:2010-09-16 01:16:12
【问题描述】:

如果我在终端输入5 5,按回车,再按回车,我想退出循环。

int readCoefficents(double complex *c){
    int i = 0;
    double real;
    double img;
    while(scanf("%f %f", &real, &img) == 2)
        c[i++] = real + img * I;


    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
    return i;
}

显然,该代码并没有为我完成这项工作(是的,我知道有一个缓冲区溢出等待发生)。

scanf 不会退出,除非我输入一个字母(或一些非数字,而不是空白字符串)。读取空行后如何让scanf退出?

【问题讨论】:

标签: c scanf


【解决方案1】:

使用 fgets 读取控制台输入:

   int res = 2;
   while (res == 2) {
       char buf[100];
       fgets(buf, sizeof(buf), stdin);
       res = sscanf(buf, "%f %f", &real, &img);
       if (res == 2)
           c[i++] = real + img * I;
   }
   c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
   return i;

【讨论】:

  • 感谢您,因为您首先提到了 fgets 和 sscanf。
  • 谢谢,刚刚意识到 c 可能不会让你在 while 循环中声明缓冲区,但概念就在那里。
  • 这个声明很好。
【解决方案2】:

您遇到的具体问题是%fscanf 格式字符串将跳过空格(包括换行符),直到找到要扫描的实际字符。来自 c99 标准:

按以下步骤执行转换规范:
-   会跳过输入空白字符(由isspace 函数指定),除非规范包含'[''c''n' 说明符。

以及在其他地方描述isspace()

标准的空白字符如下:空格' '、换页符'\f'、换行符'\n'、回车符'\r'、水平制表符'\t'和垂直制表符'\v'

最好的办法是使用fgets 获取该行(这可以很容易地防止缓冲区溢出),然后在结果行上使用sscanf

scanf 函数是您应该非常谨慎看待的函数之一。下面这段代码是我经常用来处理行输入的:

#include <stdio.h>
#include <string.h>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

 

// Test program for getLine().

int main (void) {
    int rc;
    char buff[10];

    rc = getLine ("Enter string> ", buff, sizeof(buff));
    if (rc == NO_INPUT) {
        // Extra NL since my system doesn't output that on EOF.
        printf ("\nNo input\n");
        return 1;
    }

    if (rc == TOO_LONG) {
        printf ("Input too long [%s]\n", buff);
        return 1;
    }

    printf ("OK [%s]\n", buff);

    return 0;
}

用各种组合测试它:

pax> ./prog
Enter string>[CTRL-D]
No input

pax> ./prog
Enter string> a
OK [a]

pax> ./prog
Enter string> hello
OK [hello]

pax> ./prog
Enter string> hello there
Input too long [hello the]

pax> ./prog
Enter string> i am pax
OK [i am pax]

我要做的是使用这个函数来安全地获取一条线,然后简单地使用:

sscanf (buffer, "%f %f", &real, &img)

获取实际值(并检查计数)。


其实这里有一个更接近你想要的完整程序:

#include <stdio.h>
#include <string.h>

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
    int ch, extra;

    // Get line with buffer overrun protection.
    if (prmpt != NULL) {
        printf ("%s", prmpt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;

    // If it was too long, there'll be no newline. In that case, we flush
    // to end of line so that excess doesn't affect the next call.
    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    // Otherwise remove newline and give string back to caller.
    buff[strlen(buff)-1] = '\0';
    return OK;
}

 

int main (void) {
    int i = 1, rc;
    char prompt[50], buff[50];
    float real, imag;

    while (1) {
        sprintf (prompt, "\nEnter real and imaginary for #%3d: ", i);
        rc = getLine (prompt, buff, sizeof(buff));
        if (rc == NO_INPUT) break;
        if (*buff == '\0') break;

        if (rc == TOO_LONG) {
            printf ("** Input too long [%s]...\n", buff);
        }

        if (sscanf (buff, "%f %f", &real, &imag) == 2) {
            printf ("Values were %f and %f\n", real, imag);
            i++;
        } else {
            printf ("** Invalid input [%s]\n", buff);
        }
    }

    return 0;
}

连同测试运行:

pax> ./testprog

Enter real and imaginary for #  1: hello
** Invalid input [hello]

Enter real and imaginary for #  1: hello there
** Invalid input [hello there]

Enter real and imaginary for #  1: 1
** Invalid input [1]

Enter real and imaginary for #  1: 1.23 4.56
Values were 1.230000 and 4.560000

Enter real and imaginary for #  2:

pax> _

【讨论】:

  • +1 使用scanf 读取用户的输入经常会引入这样的问题。简单地将输入读取为字符串然后解析字符串通常更不容易出错。
【解决方案3】:

有一种方法可以使用 scanf 来做你想做的事:

int readCoefficents(double complex *c) {
    int i = 0;
    double real;
    double img;
    char buf[2];
    while (scanf("%1[\n]", buf) == 0) {         // loop until a blank line or EOF
        if (scanf("%lf %lf", &real, &img) == 2) // read two floats
            c[i++] = real + img * I;
        scanf("%*[^\n]");                       // skip the rest of the line
        scanf("%*1[\n]");                       // and the newline
    }
    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
    return i;
}

如果用户只在一行上输入 1 个浮点数,它将读取下一行的第二个值。如果输入了任何随机垃圾,它将跳到换行符并在下一行重试。否则,它将继续读取浮点值对,直到用户输入空行或到达 EOF。

【讨论】:

    【解决方案4】:

    re PAXDIABLO 解决方案:它不能与用户输入的 EMPTY 行正常工作,因此应将此行添加到您的 getLine() 函数中

    if (strlen(buff) <= 1) return NO_INPUT;
    

    行后:

    if (fgets (buff, sz, stdin) == NULL)
        return NO_INPUT;
    

    所以会变成:

    ...
       if (strlen(buff) <= 1) return NO_INPUT;
       if (fgets (buff, sz, stdin) == NULL) return NO_INPUT;
    ....
    

    【讨论】:

      【解决方案5】:

      代替

      while(scanf("%f %f", &real, &img) == 2)
      

      试试

      while(scanf("%f %f%*c", &real, &img) == 2)
      
      
      scanf("%f%*c", &myfloat); // will read a float and all eventual characters after it
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-07-06
        • 2015-07-06
        • 1970-01-01
        • 2015-10-09
        • 1970-01-01
        • 1970-01-01
        • 2020-02-01
        • 2013-07-04
        相关资源
        最近更新 更多