C : scanf 和空格。我已经尝试了几乎所有的东西,目前正在学习 struct

C : scanf and whitespaces. I've tried almost everything , currently learning about struct

我搜索了整个网络,但还没有找到真正的解决方案(也许我没有用 google 搜索正确的词,我不知道)。 但我们在这里:这是我的第一个带有结构的 C 项目,我尝试使用 gets 和 puts 来克服 printf-scanf 的空格问题,但是 gets 导致了很多问题(程序直接跳到 puts , 没有 gets) 所以我进入了本论坛另一位嘉宾的话题

scanf("%[^\n]",str);

我把它应用到我的代码中,但我遇到了同样的问题,我也试过

scanf("%[^\n]s",str);
scanf("%[^\n]",&str); //despite looked senseless to me

我读过有关 fgets 和 fscanf 的内容,但我处于早期阶段

这是代码...我在 Windows 10.

上使用 Visual Studio 2015,运行
#include <stdio.h>
#include <string.h>

typedef struct s_libro {
    char titolo[50];
    char autore[50];
    char editore[20];
    int pagine;
    int costo$;
}t_libro;

int main() {
    t_libro libro1;
    t_libro libro2;
    printf("Inserire autore(max 50) ,titolo(max 50) , editore(max 20), pagine, costo$\n");
    //LIBRO 1
    printf("\nLIBRO 1\n");

    printf("\nInserisci il nome dell'autore del libro1 : \n");
    scanf("%[^\n]", libro1.autore);

    printf("\nInserire titolo del libro1 : \n");
    scanf("%[^\n]", libro1.titolo);

    printf("\nInserire nome dell'editore del libro1:\n");
    scanf("%[^\n]", libro1.editore);

    printf("\nInserire numero di pagine del libro1 : \n");
    scanf("%d", &libro1.pagine);

    printf("\nInserire il costo (dato numerico) del libro1 : \n");
    scanf("%d", &libro1.costo$);

    printf("\nLIBRO1\t");
    printf("\nTitolo Libro1\t  %s", libro1.titolo);
    printf("\nAutore Libro1\t  %s", libro1.autore);
    printf("\nPagine Libro1\t  %d", libro1.pagine);
    printf("\nEditore Libro1\t  %s", libro1.editore);
    printf("\nCosto Libro1\t  %d $", libro1.costo$);

    printf("\n\n");


    //LIBRO 2

    printf("\nLIBRO 2\n");

    printf("\nInserisci il nome dell'autore del libro2 : ");
    scanf("%[^\n]", &libro2.autore);

    printf("\nInserire il titolo del libro2 : ");
    scanf("%[^\n]", &libro2.titolo);

    printf("\nInserire L'editore del libro2:");
    scanf("%[^\n]", libro2.editore);

    printf("\nInserire il numero di pagine del libro2 : ");
    scanf("%d", &libro2.pagine);

    printf("\nInserire il costo (dato numerico) del libro2 : ");
    scanf("%d", &libro2.costo$);

    printf("\nLIBRO2\t");
    printf("\nTitolo Libro2\t  %s", libro2.titolo);
    printf("\nAutore Libro2\t  %s", libro2.autore);
    printf("\nPagine Libro2\t  %d", libro2.pagine);
    printf("\nEditore Libro2\t  %s", libro2.editore);
    printf("\nCosto Libro2\t  %d $", libro2.costo$);

    printf("\n\n");
}

如果你想保留白色 spaces,我建议使用 fgets 然后删除任何尾随的换行符,例如使用 strcspn (如 xing 所建议的)。阅读一个项目将如下所示:

printf("\nInserisci il nome dell'autore del libro1 : \n");
fgets(libro1.autore,50,stdin);
libro1.autore[strcspn(libro1.autore,"\n")] = '[=10=]';

优点是如果用户输入 "to many characters",您就不会担心缓冲区溢出;请注意,有效名称最多包含 48 个字符(+ 换行符 + 字符串终止符)。并且 - 在用户的常规输入中 - 对于可能影响任何后续 scanf("....") 语句的换行符,您是安全的。

请注意,scanf(如 scanf(" %[^\n]", libro1.autore) 格式中以 space 开头的 scanf 不允许用户输入 "empty" 名称,因为 space 中的格式会将(唯一的)换行符消耗为白色 space。没有前导 space 的 scanf,即 scanf("%[^\n]", libro1.autore) 不会消耗任何新行,从而导致您遇到的问题。

结论:在这种情况下不要使用 scanf

你有两个问题:

  1. 您没有检查 scanf() 是否成功。
  2. 您不允许 scanf() leaves the newlines in the input stream.

您有如下代码:

printf("\nInserisci il nome dell'autore del libro1 : \n");
scanf("%[^\n]", libro1.autore);

printf("\nInserire titolo del libro1 : \n");
scanf("%[^\n]", libro1.titolo);

第一个应该可以。第二个会失败,因为第一个在输入流中留下了换行符,而扫描集不认为换行符有效,所以它失败了。

有几种可能的解决方案。哪个最好取决于您的详细要求。

  1. 使用标准 C fgets() or perhaps POSIX getline() to read lines and then sscanf() 处理数据。
  2. 在格式字符串中使用 " %49[^\n]" 以跳过白色 space 包括换行符,然后再将最多 49 个非换行符读入变量。这是最小的变化。长度限制可防止缓冲区溢出。是的,格式字符串中的大小不包括空字节。
  3. 每次输入scanf()后读取该行的剩余部分。例如:

    static inline void gobble(void)
    {
        int c;
        while ((c = getchar()) != EOF && c != '\n')
            ;
    }
    

    然后:

    printf("\nInserisci il nome dell'autore del libro1 : \n");
    if (scanf("%49[^\n]", libro1.autore) != 1)
        …process error or EOF…
    gobble();
    
    printf("\nInserire titolo del libro1 : \n");
    if (scanf("%49[^\n]", libro1.titolo) != 1)
        …process error or EOF…
    gobble();
    

毫无疑问,还有很多其他选择;您将必须决定什么最适合您。请注意,您应该每次使用它时检查scanf()的状态,并且您应该确保不允许溢出.


顺便说一句,scanf("%[^\n]", &str) 变体是错误的。传递的参数类型是 char (*)[50](如果变量是 char str[50];),但扫描集转换规范需要一个 char *——一个完全不同的类型。有一个怪癖意味着你可以侥幸逃脱; str&str 的地址值相同,但类型不同,查看 (str + 1)&str + 1 可以注意到——值将完全不同.正确的表示法是 scanf("%[^\n]", str),因此(模数添加溢出保护)。