在C中:如何确保用户输入是单个整数?

nhjlsmyf  于 2022-10-17  发布在  Unix
关注(0)|答案(2)|浏览(189)

(作业)
这个程序接受用户的一个整数作为输入,并显示斐波纳契数列的那么多个数字(使用在Unix中创建的子进程)。对于我的任务,程序还需要执行错误检查以确保输入是有效的:参数的数量应该是正确的,给定的数量应该是正整数。
我不知道如何验证用户输入的数字不是小数,也不知道如何阻止用户输入由空格分隔的多个参数(例如:1127)。
如果这是一个愚蠢的问题,请原谅我,但这是我第一次使用C。谢谢你能提供的任何帮助!


# include <stdio.h>

# include <sys/types.h>

# include <unistd.h>

# include <sys/wait.h>

int main()
{
    int a = 0, b = 1, n = a + b, i; // decalre global variables and initialize some of them

    printf("Enter the number of a Fibonacci Sequence:\n"); // print message to the terminal asking user to enter a number 

    scanf("%d", &i); // (& is used to get the addresss of a variable) scan user input and store at the address of i (i = user input)

    if (i <= 0) {
        printf("please only enter an integer number greater than 0\n");
        return 1;
    }

    // check number of arguments 

    // check that a float was not entered 

    // printf("first (before pid_t): id = not yet declared, parent pid = %d\n", getpid()); // TEST

    // the return value from the fork system call: 
    // for the child, its value is 0. For the parent, it's the actual pid of the child
    pid_t id = fork(); // (pid_t integer type that can rep a processes ID) creates a child process from the original and sets id equal to the return value of the fork 

    // printf("second (after pid_t, in child process): id = %d, pid = %d\n", id, getpid()); // TEST

    if (id < 0) { // if id < 0 an error occured 
        fprintf(stderr, "Fork Failed"); // stderr is the standard error message to print output to terminal. fprintf is the format print
        return 1; // retrun 1 to exit the program 
    }

    if (id == 0) // if id == 0 then we know this is the child process
    {
        //printf("third (in child, after testing id value): child id = %d, pid = %d\n", id, getpid()); // TEST 

        printf("child: the first %d numbers in the fibonnaci sequence are:\n", i); // print a message with the number entered by the user

        if (i == 1) {
            printf("%d", 0);
            return 0;
        }
        else {
            printf("%d %d", 0, 1);
            i -= 2;
        }

        while (i > 0) {
            n = a + b;
            printf(" %d", n);
            a = b;
            b = n;
            i--;
        }
    }
    else // if cpu goes back to parnet before child has completed, tell parent to wait until child has completed
    {
        printf("Parent is waiting for child to complete...\n");

        waitpid(id, NULL, 0); // suspends the calling process until the child pricess ends or is stopped. First parameter is the pid to wait for, the others aren't relevant here 

        printf("\nParent: the child process is done\n");

        //printf("fourth (in else clause): id = %d, pid = %d\n", id, getpid()); // TEST
    } 

    return 0; // program has finished and exited without error. Main must return an int
}
d5vmydt9

d5vmydt91#

首先,您可能想要清楚地定义您想要/不想要禁止哪些类型的输入。以下是一张图片,说明了所有这些问题:

"xxx   -123456789012.345e67   yyy\n"
  ^  ^ ^    ^     ^   ^  ^  ^  ^  ^
  |  | |    |     |   |  |  |  |  |
  |  | |    |     |   |  |  |  |  +-- trailing \n
  |  | |    |     |   |  |  |  +----- trailing non-numeric garbage
  |  | |    |     |   |  |  +-------- trailing whitespace
  |  | |    |     |   |  +----------- exponent
  |  | |    |     |   +-------------- fractional part
  |  | |    |     +------------------ more digits than fit in 32-bit int
  |  | |    +------------------------ normal integer
  |  | +----------------------------- sign
  |  +------------------------------- leading whitespace
  +---------------------------------- leading non-numeric garbage

对于一个完整的解决方案,对于每一个部分,你必须决定是接受它们是合法的,还是拒绝它们,认为它们是非法的,或者悄悄地忽略它们,认为它们“不在乎”,或者希望它们不会发生。
scanf可以处理其中的三到四个。strtol可以处理其中的大部分,或者如果您在兼职时多做一点工作,可以处理几乎所有的工作;或者,如果您在兼职时做更多的工作,则可以处理所有的工作。
(我假设您只对十进制或以10为基数的输入感兴趣。如果您想读取十六进制或二进制,您还必须决定是否接受前导的0x0b。)
[免责声明:是的,这基本上是一条延伸的评论,而不是回答。]

42fyovps

42fyovps2#

scanf返回成功读取和分配的项目数。因此,第一步是检查scanf的返回值:

if ( scanf( "%d", &i ) == 1 )
  // process i
else
  // bad input

我们针对1进行测试,因为我们尝试读取一项。
%d说明符告诉scanf跳过任何前导空格,然后一直读到下一个非数字字符。这意味着它将拒绝以非数字开头的输入,如abca23.333等;对于这些输入,它将返回0,并且不向i写入任何内容。
但是..。
对于像12.3这样的输入,它会将12转换并赋值给i,并返回1.3留在输入流中,以扰乱下一次读取
此外,如果您尝试使用%d读取另一个输入,它将立即失败,因为.3中的前导.;如果您不设法从输入流中删除那个有问题的字符,您将永远不会使用%d读取它。
scanf是很棒的,当你知道你的输入永远是良好的,这意味着它不是一个很好的交互输入工具。
我的首选方法是将输入作为文本读取,然后尝试使用strtol/strtoul(对于整型)或strtod(对于浮点型)进行转换。快速、肮脏、未经测试的例子:


# include <stdio.h>

# include <stdlib.h>

# include <string.h>

# include <ctype.h>

# define BUFSIZ 128

/**
 * Reads the next integer from the specified stream and stores
 * it to *var.  Returns 1 (true) for success, 0 (false) for failure.  
 * If we return 0, the contents of *var are unchanged.
 */
int getNextInt( FILE *stream, int *var )
{
  char inbuf[BUFSIZ+1] = {0};

  /**
   * Read input as text
   */
  if ( fgets( inbuf, sizeof inbuf, stream ) )
  {
    /**
     * Look for a newline - if one isn't present, 
     * the input was too long for our buffer.  We
     * reject the input out of hand, but first we
     * need to consume the extra characters including
     * the newline.
     */
    char *newline = strchr( inbuf, '\n' );
    if ( !newline )
    {
      fputs( stderr, "Input too long - rejecting\n" );
      while ( fgetc( stream ) != '\n' )
        ; // empty loop
      return 0;
    }

    /**
     * Overwrite the newline with the string terminator.
     */
    *newline = 0;

    /**
     * Use strtol to convert the text to an integer
     * value. chk will point to the first character
     * *not* converted - if this character is anything
     * but whitespace or a 0, the input is not a valid
     * integer.  Store the result to a temporary variable
     * until we're sure it's valid.
     */
    char *chk;
    int tmp = strtol( inbuf, &chk, 10 );
    if ( !isspace( *chk ) && *chk != 0 )
    {
      fprintf( stderr, "%s is not a valid integer!\n", inbuf );
      return 0;
    }
    *var = tmp;
    return 1;  
  }
  fputs( "Error on fgets!\n", stderr );
  return 0;
}

相关问题