fgets 在 scanf [重复] 后不起作用

问题描述 投票:0回答:7
#include <stdio.h>
#include <string.h>
#include <ctype.h>

void delspace(char *str);

int main() {
    int i, loops;
    char s1[101], s2[101];

    scanf("%d", &loops);

    while (loops--) {
        fgets(s1, 101, stdin);
        fgets(s2, 101, stdin);
        s1[strlen(s1)] = '\0';
        s2[strlen(s2)] = '\0';

        if (s1[0] == '\n' && s2[0] == '\n') {
            printf("YES\n");
            continue;
        }

        delspace(s1);
        delspace(s2);

        for (i = 0; s1[i] != '\0'; i++)
            s1[i] = tolower(s1[i]);

        for (i = 0; s2[i] != '\0'; i++)
            s2[i] = tolower(s2[i]);

        if (strcmp(s1, s2) == 0) {
            printf("YES\n");
        }
        else {
            printf("NO\n");
        }
    }

    return 0;
}

void delspace(char* str) {
    int i = 0;
    int j = 0;
    char sTmp[strlen(str)];

    while (str[i++] != '\0') {
        if (str[i] != ' ') {
            sTmp[j++] = str[i];
        }
    }
    sTmp[j] = '\0';
    strcpy(str, sTmp);
}

我输入“loops”后,“s1”自动分配了一个空行。它是如何发生的?我确定我的键盘工作正常。

c scanf fgets
7个回答
27
投票

scanf()
完全按照您的要求读取,将以下
\n
从该行的末尾留在缓冲区中,
fgets()
将读取它。要么做一些事情来消耗换行符,要么(我的首选解决方案)
fgets()
然后从该字符串中获取
sscanf()


9
投票

scanf
在输入缓冲区中留下空白,包括换行符。要使用 fgets 读取下一行,您需要手动删除当前行的其余部分:

int c;
do{
    c = getchar();
}while(c != EOF && c != '\n');

7
投票

这是一个更简单的解决方案

scanf("%d",&loops);
while ((getchar()) != '\n'); //This will consume the '\n' char
//now you're free to use fgets
fgets(string,sizeof(string),stdin);

2
投票

Geekosaur answered 你的问题很好,我只是指出你的代码的另一个“问题”。

如果

s1[strlen(s1)] = '\0';
在执行之前已经正确地以 null 终止,则 s1 是一个
no-op

但是,如果

s1
在该行执行之前还没有正确地以 null 终止(而且你很不幸),它将导致:

  • a SIGSEGV 在 POSIX (*nix) 系统上。
  • a GPF 在 Windows 上。

这是因为

strlen
基本上找到existing null-terminator的索引并返回它!这是一个有效的、未优化的 strlen 实现:

int strlen(char *string) {
    int i = 0;
    while(string[i] != '\0') {
        ++i;
    }
    return i;
}

所以......如果你真的担心字符串没有被空终止那么你会做这样的事情:

  • string[sizeof(string)]='\0';
    local 自动字符串(编译器“知道”字符串的大小);
  • string[SIZE_OF_STRING]
    对于所有其他字符串,其中
    SIZE_OF_STRING
    (最常见)是一个
    #define
    'd 常量,或者您维护的专门用于存储动态分配字符串的当前 SIZE(不是长度)的变量。

如果你真的、真的、真的担心字符串没有以 null 结尾(就像你正在处理“肮脏”的库方法(例如 Tuxedo 的 ATMI)你ALSO“清除”你的“返回字符串”在将它们传递给可疑的库方法之前:

  • 之前:
    memset(string, NULL, SIZE_OF_STRING);
  • 调用:
    DirtyFunction(/*out*/string)
    ;
  • 之后:
    string[SIZE_OF_STRING]='\0'

SIG11 是一个完整的 biatch,因为(除非你用 signal-processor “挂钩”它们,否则,它们会导致 Unix 硬终止你的程序,所以你不能记录任何东西(事后)到帮助弄清楚到底是从哪里来的……特别是考虑到在许多情况下,抛出 SIG11 的代码行离字符串丢失其 null 的实际原因不远-终结者。

这对你有意义吗?

PS:警告:

strncpy
并不总是空终止......你可能意味着
strlcpy
。我通过艰难的方式学到了这一点......当 6000 万美元的账单运行崩溃时。


编辑:

仅供参考:这是 strlen 的“安全”(未优化)版本,我称之为

strnlen
(我认为这应该在 string.h 中。唉。)。

// retuns the length of the string (capped at size-1)
int strnlen(char *string, int size) {
    int i = 0;
    while( i<size && string[i]!='\0' ) {
        ++i;
    }
    return i;
}

0
投票

就放

scanf("%d\n",&loops);

代替

scanf("%d",&loops);


-1
投票

另一种在变量标签循环中扫描整数后忽略后续换行符(由于按下 ENTER)的方法是:

scanf ("%d%*c", &loops);

在哪里,根据手册页:

* 禁止赋值。随后的转换照常进行,但不使用指针;转换的结果被简单地丢弃。

这不太可能,但是在 scanf 期间检查错误的好习惯:

errno = 0
scanf ("%d%*c", &loops);
if (errno != 0) perror ("scanf");
// act accordingly to avoid un-necessary bug in the code

例如在你的代码中,loops是一个本地未初始化的变量,包含垃圾。如果 scanf 未能填充所需的值,则后面的 while 循环可能会任意运行。


-1
投票

scanf()
留下以下内容 从缓冲区中该行的末尾开始,
fgets()
将读取它。为了解决这个问题,我们必须做一些事情来消耗换行符。让我们看看问题。

问题

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    scanf("%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
}
输出:
Enter in scanf: Hello
Enter in fgets:

如您所见,它没有显示 fgets 部分。让我们看看解决方案。

getchar()
放在scanf和
fgets()

之间
#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    scanf("%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
}
输出:
Enter in scanf: Hello
Enter in fgets: Hello

如果可能的话,在
scanf()
之后使用
fgets()

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    fgets(input, 10, stdin);
    getchar();
    printf("Enter in scanf: ");
    scanf("%s", input);
}
输出:
Enter in fgets: Hello
Enter in scanf: Hello

fget()
2次(这种方法有时不起作用)

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    scanf("%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
    fgets(input, 10, stdin)
}
输出:
Enter in scanf: Hello
Enter in fgets: Hello

sscanf()
插入
scanf()

#include<stdio.h>
void main()
{
    char input[10];
    printf("Enter in fgets: ");
    sscanf(hello, "%s", input);
    getchar();
    printf("Enter in scanf: ");
    fgets(input, 10, stdin);
}
输出:
Enter in sscanf: Hello
Enter in fgets: Hello
© www.soinside.com 2019 - 2024. All rights reserved.