使用gcc编译器,建议替换gets()函数的另一种方法是什么?

3

我尝试在程序的字符串数组中输入多个字符串,使用了以下代码:

scanf("%80[^\r\n]", strings[i]);

fgets(string[i], MAXLEN, stdin);

还使用了一个自定义函数:

int getString(char s[]) {

    char ch;
    int i=0;

    while( (ch = getchar()) != '\n'   &&   ch != EOF ) {
        s[i] = ch;
        ++i;
    }

    s[i] = '\0';

    fflush(stdin);

    return i;
}

但无法获取包含空格的多个字符串输入

函数gets()以前对我有用,但是由于已经过时,找不到替代方法

这就是它被使用的地方:

int getString(char s[]) {

char ch;
int i=0;

while( (ch = getchar()) != '\n'   &&   ch != EOF ) {
    s[i] = ch;
    ++i;
}

s[i] = '\0';

fflush(stdin);

return i;
}


struct vechileData
{
char vechileType[MAXLEN];
int begin_month;
int end_month;
    double price;
} data[5];


int main(int argc, char const *argv[])
{
printf("Input Vechile data: \n");

int i=0;
while(i < 5) {
    printf("Input vechile Type : \n");
    fgets(data[i].vechileType, MAXLEN, stdin);

    printf("Input begin month : \n");
    scanf("%d", &data[i].begin_month);

    printf("Input end monhth : \n");
    scanf("%d", &data[i].end_month);

    printf("Input price : \n");
    scanf("%lf", &data[i].price);

    ++i;
}

printf("Input Vechile Type to display information about the vechile : \n");
char vech[MAXLEN];
fgets(vech, MAXLEN, stdin);

i=0;
while(i < 5) {
    if (strcmp(vech,data[i].vechileType) == 0)
    {
        printf("vechileType: %s\n", data[i].vechileType);
        printf("Begin month: %d\n", data[i].begin_month);
        printf("End month: %d\n", data[i].end_month);
        printf("Price : %lf\n", data[i].price);
    }
    ++i;        
}

return 0;
}

在运行时,它似乎跳过了下一个输入到字符串语句。

1
在标准输入上使用 fflush() 是一个不好的主意,因为刷新只对输出流有明确定义。 - unwind
你的实际问题是什么?fgets调用应该可以正常工作并完成正确的操作。如果它不起作用,你可能声明字符串时有错误。 - Sebastian Redl
1
你能展示一下用fgets的不正确样例吗?我问这个是因为它应该可以给你一个完整的行直到 \n,也许你在其他地方有错误。 - Nicholaz
gets的替代方案是fgets - Some programmer dude
1
不要因为gets()已被弃用就放弃它,而是应该因为它很危险而放弃使用。 - Oswald
此问题的替代方案已在此问题中得到解答。 - phuclv
5个回答

7

你的问题并不是 gets() 的问题。

scanf("%d", ...)scanf("%lf", ...) 都没有消耗数字后的 '\n',因此不会导致你的问题。下一次从 stdin 读取时会读取 '\n'。所以当读取下一个车型时,它会读取到残留的 '\n'。你的第二个车型最终变成了 "\n"

使用 fgets(data[i].vechileType, MAXLEN, stdin); 会在 data[i].vechileType 中加入一个 '\n'。你可能不想要这个。你之前使用的 gets() 消耗了但并没有将 '\n' 放在返回值中。

我早就放弃了使用 scanf() 进行用户输入,因为这些微妙的问题。

建议将输入和解析分开,使用 fgets(),然后使用 sscanf()。示例:

char number[80];
if (fgets(number, sizeof(number), stdin)) {
  sscanf(number, "%d", &x)

您对 gets() 的替代实现与原来的有以下不同之处:

1) 不返回 s(或 NULL 或错误/文件结束)
2) 在文件结束时不会设置 eof 指示器。
3) 如果 getchar() 返回一个 '\0',您的 while 循环会错误地继续执行。


建议如果必须替换 gets(),请使用 fgets() 进行替代。

#define My_gets_N (1024 /* Some BA number */)

char *My_gets(char * str) {
  char buffer[My_gets_N];
  char *retval = fgets(buffer, sizeof(My_gets_N), stdin);
  if (retval) {
    int l = strlen(buffer);
    /* fgets() saves '\n', but gets() does not */
    if ((l > 0) && (buffer[l-1] == '\n')) {
      l--;
    }
    memcpy(str, buffer, l);
    str[l] = '\0';
    return str;
  }
  else {
    return 0;
  }
}

如果您需要处理长度大于固定My_gets_N的字符串,请使用其他编码来解决替代方案。

2

关于 fgets() 方法出了问题,你需要更具体地说明是什么问题,因为我会建议使用这个方法,而且它确实有效。

请注意,fgets() 会输入整行内容,包括末尾的换行符和回车符,如果你不需要保留它们,可能需要将其清除。


确切地说,这就是问题所在。我必须搜索整个字符串以查找 '\n' 字符,然后用 '\0' 替换它。你有没有其他替代方案,我的意思是将换行符替换为 null? - amarVashishth

1
我不明白为什么你会使用gets(),因为几乎所有的C书籍在K&R之后都会警告它已经过时且极其危险。就像其他人所说的那样,如果你正确使用fgets(),它肯定会起作用。

是的,非常危险。gets()函数被定义为将输入读入由调用者提供的缓冲区中,直到第一个换行符(或EOF)为止。它不提供任何机制来限制输入到缓冲区的大小,gets()将读取的唯一限制是“换行符或EOF”。至少,fgets()函数允许您指定正在使用的缓冲区的大小。 - This isn't my real name

0

不要用 gets 替换所有使用它的实例,而是使用以下宏:

#define TRUNCATE_NULL(strText) \
 { \
   int _strlen = strlen(strText); \
   if (_strlen > 0 && strText[_strlen - 1] == '\n') strText[_strlen - 1] = '\0'; \
   else while(fgetc(stdin)!='\n'); \
 }
#define gets(strText) fgets(strText, sizeof(strText), stdin); TRUNCATE_NULL(strText);

为什么要使用fgets? 因为它比gets更安全。
gets真的不安全吗? 是的。它确实很贪婪,会接收你给的所有食物,即使不能吃。 所以从技术上讲,正如@halfer在下面的评论中指出的那样, 使用gets时,程序容易发生缓冲区溢出。
怎么回事? char name[5]; gets(name); 现在提供超过5个字符的输入,它会接受它。 这将覆盖内存中的数据,这种方法不应该这样覆盖。
好了,用fgets可以,但为什么要使用TRUNCATE_NULL宏? fgets也不完美。它将接受\n(换行符)作为要放入输入名称的字符。 因此,为了去除不必要的\n,并确保gets的预期功能得到实现,我们可以使用它。

1
我已经调整了语法,并去除了一些不必要的粗俗语言。这样比较容易理解,您说的“尽可能接受您可以提供的食物”是什么意思?如果这是对缓冲区溢出的引用,可能有更好的表达方式! - halfer
谢谢@halfer,我回答这里的时候可能是饿了。 - prashant
好的,请随意重新表述,如果您能的话! - halfer

0

实际上,你可以使用while((getchar())!='\n');来避免这种类型的问题,而且没有必要使用fflush(stdin)函数。以下是你可以使用的代码

#include<stdio.h>
#include<string.h>
#define MAXLEN 50
int getString(char s[]) 
{
    char ch;
    int i=0;
    while( (ch = getchar()) != '\n'   &&   ch != EOF )
    {
        s[i] = ch;
        ++i;
    }
    s[i] = '\0';
    return i;
}
struct vechileData
{
    char vechileType[MAXLEN];
    int begin_month;
    int end_month;
    double price;
}data[5];
int main(int argc, char const *argv[])
{
    printf("Input Vechile data: \n");
    int i=0;
    while(i < 2)
    {
        printf("Input vechile Type : \n");
        fgets(data[i].vechileType, MAXLEN, stdin);

        printf("Input begin month : \n");
        scanf("%d", &data[i].begin_month);

        printf("Input end monhth : \n");
        scanf("%d", &data[i].end_month);

        printf("Input price : \n");
        scanf("%lf", &data[i].price);
        while((getchar())!='\n');
        ++i;
    }    
    printf("Input Vechile Type to display information about the vechile : \n");
    char vech[MAXLEN];
    fgets(vech, MAXLEN, stdin);
    i=0;
    while(i < 2)
    {
        if (strcmp(vech,data[i].vechileType) == 0)
        {
            printf("vechileType: %s\n", data[i].vechileType);
            printf("Begin month: %d\n", data[i].begin_month);
            printf("End month: %d\n", data[i].end_month);
            printf("Price : %lf\n", data[i].price);
        }
        ++i;
    }
    return 0;
}

希望这能对你有所帮助......


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接