【问题标题】:C fgets and sscanf in loop : prevent useless looping循环中的 C fgets 和 sscanf :防止无用的循环
【发布时间】:2014-09-25 09:17:11
【问题描述】:

我在循环和fgetssscanf 获取输入时遇到问题。

我知道问题在于输入的 malloc 的大小。如果用户输入的数字大于 malloc,我想再次要求输入一个新数字。 但是在这段代码中,如果用户输入的数字太大,我认为它会循环很多时间(单词的大小 / 8)。

例如如何在不循环 4 次的情况下再次要求用户输入新号码。 请参阅我用大数字制作的示例。

这个想法是在循环之后释放输入,但它不起作用。有什么想法吗?

这是我的代码:

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>


int main(void) {
    char x[8];
    char y[8];
    int result = 0;
    char *input=malloc(sizeof(char)*8);
    bool answer = 0;
    char *pos;

    while(!answer) {

        fgets(input, sizeof(input)-1, stdin);
        //remove the /n from fgets
        if ((pos=strchr(input, '\n')) != NULL)
            *pos = '\0';

        result = sscanf (input, "%s %s", x, y);
        printf("%d\n", result);
        if(result < 2) {
            fprintf(stderr, "There is an error with the number you give, try again\n");
        } else {
            printf("%s\n", x);
            printf("%s\n", y);
        }

    }
    return 0;
}

输出为:“01 01”

01 01
2
01
01

输出:000000005 000000005

0000000000005 000000000000005
1
There is an error with the number you give, try again
1
There is an error with the number you give, try again
2
5
0000
1
There is an error with the number you give, try again
1
There is an error with the number you give, try again

【问题讨论】:

  • sizeof(input) 是指针的大小,而不是 malloc 内存的大小。你很幸运,两者都是 8 的机会很好。
  • 假设您将input 缓冲区修复为动态增长以消耗您的行内容,您会意识到尝试将sscanf"0000000000005" 这样的字符串转换为char[8] 时遇到了一个全新的问题,对?我的意思是,您正试图将 14 个字符推入一个只能容纳 8 个字符的缓冲区,而 sscanf 在您使用它时完全没有意识到这个限制。

标签: c fgets scanf


【解决方案1】:

fgets() 当它比它的缓冲区长时,它不会丢弃其余的行。你必须自己做。

如果您查看我经常与fgets 一起使用的这段代码,您会看到两个任务是分开的,以及在什么情况下完成了哪个任务:

/*Returns 0 if OK, a negative value if EOF.*/
int fpurge(FILE *f)
{
    int c;
    while((c=fgetc(f))!=EOF && c!='\n')
    { }
    return (c==EOF ? -1 : 0);
}

/* Returns a nonzero value if found, zero if not. */
int truncate_newline(char *str)
{
    int bRet=0;
    if(str!=NULL)
    {
        char *pNewLine = strchr(str, '\n');
        if(pNewLine!=NULL)
        {
            bRet = 1;
            *pNewLine = '\0';
        }
    }
    return bRet;
}

/* Returns 0 if buffer is full, a positive value if line is complete,
   a negative value if EOF (implies buffer full). */
int fclean(char *str, FILE *f)
{
    int ret = 1;
    if(!truncate_newline(str))
        ret = fpurge(f);
    return ret;
}

您可以看到您自己的代码执行truncate_newline 部分,但不是“丢弃其余行”(此处为函数fpurge)部分。

如果您这样更改代码,它应该可以工作:

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>

#define BUFFER_SIZE 8

int main(void) {
    char x[BUFFER_SIZE];
    char y[BUFFER_SIZE];
    int result = 0;
    char *input=calloc(BUFFER_SIZE, sizeof(char));
    bool answer = 0;
    char *pos;

    while(!answer) {

        fgets(input, BUFFER_SIZE, stdin);
        //remove the /n from fgets
        if ((pos=strchr(input, '\n')) != NULL)
            *pos = '\0';
        else
        {
            int c;
            while((c=getchar())!='\n' && c!=EOF) {}
        }

        result = sscanf (input, "%s %s", x, y);
        printf("%d\n", result);
        if(result < 2) {
            fprintf(stderr, "There is an error with the number you give, try again\n");
        } else {
            printf("%s\n", x);
            printf("%s\n", y);
        }

    }
    return 0;
}

或者简单地将整个 if() 替换为 fclean(input, stdin);

【讨论】:

  • 感谢您的解释!同样在您的代码中,将char *pNewline = strchr(str, '\n'); 替换为char *pNewLine = strchr(str, '\n');
  • @martialdidit PS:fgets() 的大小参数中已经包含了空终止符,你不需要自己减去 1。
  • 不检查来自fgets() 的返回值是个问题。如果fgets() 返回NULLinput 的状态没有很好的定义。
  • @chux 是的,我直接用#define 替换了魔法大小,这样问题就少了。并将缓冲区初始化为零以减少“fgets failed”问题,尽管我无法通过检查其返回值来实际解决它。
  • 出现罕见的读取错误,“...数组内容不确定,返回空指针。”。 C11 §7.21.7.2 检查fgets() 结果是唯一剩余的定义结果。
猜你喜欢
  • 1970-01-01
  • 2013-08-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
  • 2012-10-18
  • 1970-01-01
  • 2012-05-08
  • 1970-01-01
相关资源
最近更新 更多