在不使用 scanf 的情况下获取开关的整数

Posted

技术标签:

【中文标题】在不使用 scanf 的情况下获取开关的整数【英文标题】:Getting integer for a switch without using scanf 【发布时间】:2022-01-09 07:08:26 【问题描述】:

所以我编写了一些具有switch 的代码,我需要给它一个整数以在switch 中选择case。我不能使用scanf(),因为我有多个fgets(),而且scanf() 输入中的'\n' 会破坏代码。

这是我的代码:

ma​​in.c

#include "functions.h"
#include <stdlib.h>

int main() 
    int choice;
    char temp[10];
    
    do 
        printf("Menu\n\n");
        printf("1. Read student information from file\n");
        printf("2. Write student information to file\n");
        printf("3. Exit\n");
        fgets(choice, 10, stdin);
    
        switch (choice) 
          case 1:
            fileRead();
            break;
          case 2:
            fileWrite();
            break;
          default:
            printf("Program stopped!\n");
            break;
        
     while (choice != 3);
    return 0;

functions.h

#ifndef UNTITLED17_FUNCTIONS_H
#define UNTITLED17_FUNCTIONS_H

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

struct student 
    char studentID[100];
    char studentName[100];
    char age[100];
 student_t;

void fileRead() 
    FILE *f = fopen("student_read.txt", "r");
    if (f == NULL) 
        printf("Failed to open file(s)!\n");
    
    printf("Type your student ID:");
    fgets(student_t.studentID, 100, stdin);

    printf("Type your name:");
    fgets(student_t.studentName, 100, stdin);

    printf("Type your age:");
    fgets(student_t.age, 100, stdin);

    printf("Student id: %s\n", student_t.studentID);
    printf("Name: %s\n", student_t.studentName);
    printf("Age: %s\n", student_t.age);


void fileWrite() 
    FILE *f = fopen("student_write.txt", "w");
    if (f == NULL) 
        printf("Failed to open file(s)!\n");
    
    printf("Type your student ID:");
    fgets(student_t.studentID, 100, stdin);
    printf("Type your name:");
    fgets(student_t.studentName, 100, stdin);
    printf("Type your age:");
    fgets(student_t.age, 100, stdin);

    printf("Student id: %s\n", student_t.studentID);
    printf("Name: %s\n", student_t.studentName);
    printf("Age: %s\n", student_t.age);


#endif //UNTITLED17_FUNCTIONS_H

有什么想法吗?

谢谢:)

【问题讨论】:

使用fgetssscanf 缓冲区,然后其他fgets 调用将按预期工作。 不要将函数定义放入h 文件中。它仅用于声明(原型)。 编译器没有抱怨fgets(choice, 10, stdin);? 没有什么可以阻止您使用scanf,但您需要正确使用它。如果您想使用换行符,scanf("%d",...) 不会这样做,但没有什么可以阻止您以其他方式使用它。 (例如,循环调用fgetc @Ole-Johan 不过,恭喜您发现了scanf/fgets 隔行扫描问题。我们每天都会收到几十个问题,询问为什么 fgets 不起作用。这是我记得第一次看到一个已经知道这个问题并试图做得更好的人提出的问题。 【参考方案1】:

作为 OP mentions 输入的合格性质,我会选择 simple fgets(), atoi()。读入缓冲区并转换为int

//fgets(choice, 10, stdin);
//switch(choice)

// Initialize just in case no input.
// Not so big to prevent atoi() overflow - which is UB. 
char buf[5] = ""; 

fgets(buf, sizeof buf, stdin);
choice = atoi(buf);
switch(choice) 

如果输入不合格,我会使用更大的缓冲区、strtol() 和更多的错误检查。

在这种情况下,创建一个辅助函数来获取int 是有意义的。

一些未经测试的代码:

#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

// On EOF, return EOF
// Else return 1 for success, 0 failure.
int getint(const char *prompt, int *dest) 
  // Some buffer generously sized to the type.
  char buf[sizeof *dest * CHAR_BIT];

  if (prompt) 
    fputs(prompt, stdout);
  
  if (fgets(buf, sizeof buf, stdin) == NULL) 
    return EOF;
  
  char *endptr;
  errno = 0;
  long val = strtol(buf, &endptr, 0);
  if (buf == endptr) 
    return 0;  // No conversion
  
  if (errno == ERANGE) 
    return 0;  // out of long range
  
  #if LONG_MIN < INT_MIN || LONG_MAX > INT_MAX
  if (val > INT_MAX || val < INT_MIN) 
    errno = ERANGE;
    return 0; // Out of int range
  
  #endif
  *dest = (int) val;
  return 1;

【讨论】:

哎呀!我的错,没问题。【参考方案2】:

fgets,正如s 所暗示的那样,用于从文件中读取字符串,这就是编译器抱怨第一个参数的原因:它应该是指向那个位置的指针字符串会去。在那里,正如@Cheetah 建议的那样,解析它以获得它所代表的整数值。

【讨论】:

【参考方案3】:

您的代码中的这一行将一行读入choice 缓冲区:

fgets(choice, 10, stdin);

你可以在之后直接添加:

int n;
if (sscanf(choice, "%d", &n) != 1)) 
    fprintf(stderr, "Invalid input\n");
    exit(1);

最值得注意的是 sscanf 读取应该包含字符串的 choice 缓冲区,因为它是由 fgets 返回的。该值存储在n 中。您可以在 switch 声明中查看。

我在sscanf 周围添加了一个if,以检查是否返回了1,即成功读取的参数数量。

【讨论】:

我最终这样做了:int choice; char choiceTemp[10]; fgets(choiceTemp, 10, stdin); sscanf(choiceTemp, "%i", &amp;choice); 是的,应该可以完成这项工作。但请注意,人们可能会输入无效的行。 是的,输入是预先确定的,因为程序是使用 python 程序测试的,所以我有一些余地 choiceint,不是任何类型的缓冲区。 @ScottHunter 我对编码非常陌生,所以我真的不知道这意味着什么,但至少它有效【参考方案4】:

fgets(choice, 10, stdin); 绝对不正确,因为 choiceint,不是指向 char 的指针,也不是 char 数组。

你应该这样读取字符串并将其内容转换为整数:

    if (!fgets(temp, sizeof temp, stdin))
        return 1;
    choice = atoi(temp);

还要注意这些备注:

如果文件无法在fileRead()fileWrite()中打开,请在输出错误信息后返回。 函数不应该在头文件中定义,只有声明属于那里 您应该测试fgets() 的返回值以检测文件结束。 fileReadfileWrite 应该返回成功/失败指示符。 你应该去掉数组末尾fgets() 左边的换行符。更好的是:您应该编写一个函数,将一行读入目标数组,截断到可用空间并去除初始和尾随空格。

【讨论】:

是的,我通过为 fgets 制作一个临时字符并使用 sscanf 转换它来修复它

以上是关于在不使用 scanf 的情况下获取开关的整数的主要内容,如果未能解决你的问题,请参考以下文章

如何在不获取“TypeError:字符串索引必须是整数”的情况下对图像进行 numpy 切片

如何在不使用 InterfaceBuilder 的情况下将 UISwitch(切换开关)添加到 UIToolBar

在不使用地图Android的情况下获取用户(纬度,经度)位置[重复]

如何在不使用 transform: scale 的情况下更改 CSS 切换开关的大小?

如何在不登录的情况下使用 google api 获取分析数据?

在不使用 Internet 的情况下获取下一分钟时间 (UTC)