簡體   English   中英

驗證 scanf 中的最大整數

[英]Validate max integer in scanf

我想從 stdin 讀取一個 int 但我想驗證用戶是否超過了 int 最大值。 我該怎么做?

int n;
scanf("%d", &n);

scanf 讀取十進制輸入並存儲在 int 中,導致溢出。 我該如何檢查和避免這種情況?

將數字的字符串表示形式轉換為實際值並監視溢出的唯一方法是使用strto.. group中的函數。 在您的情況下,您需要讀取數字的字符串表示,然后使用strtol函數轉換它。

請注意建議使用atoisscanf執行最終轉換的響應。 這些功能都不能防止溢出。

另一種方法是將最大數字設置為解析。

例如:

  int n;
  scanf("%5d", &n); // read at most 5 digits
  printf("%i\n", n);

想到兩種方法。

如果您知道輸入是僅類似整數的輸入,但是“略微”超出整數范圍,則第一個是有用的:

long x;
if (1 != scanf("%ld", &x))
         error "not a number or other i/o error"
else
if (x < -MAXINT ||  x > MAXINT)
        error "exceeds integer range"
else    printf "it is an integer";

這對編譯器和平台有很大的依賴性。 請注意,C語言僅保證long大於或等於 int的大小。 如果這是在longint大小相同的平台上運行,那么這是一種無用的方法。

第二種方法是將輸入作為字符串讀取並直接解析:

char buf [1000];
if (1 != scanf ("%1000s", buf))
        error "i/o error";
size_t len = strspn (buf, "0123456789+-");
if (len != strlen (buf))
        error "contains invalid characters";
long  number = 0;
int   sign = 1;
for (char *bp = buf;  *bp;  ++bp)
{
          if (*bp == '-')
          {
               sign = -sign;
               continue;
          }
          if (*bp == '+')
               continue;
          number = number * 10  +  *bp - '0';
          if (number > MAXINT)
               error "number too large";
}
if (sign < 0)
         number = -number;

這段代碼有幾個缺點:它取決於long大於int ; 它允許加號和減號出現在字符串中的任何位置。 它可以很容易地擴展到允許基數十以外的數字。

可能的第三種方法可能是輸入字符串檢查字符集並使用double轉換來進行范圍檢查。

根據ISO C11 標准的 §7.21.6.2 ¶10 ,如果無法表示函數scanf執行的轉換結果,則行為未定義。 因此,該函數不能可靠地用於輸入驗證的目的。

但是,當使用函數strtol將字符串轉換為整數時,如果發生范圍錯誤,則行為是明確定義的。 在這種情況下, errno將設置為ERANGE 這將告訴您用戶輸入的數字是否超出long的范圍。 如果要確定它是否超出int范圍,則必須另外驗證該數字是否大於INT_MIN且小於INT_MAX 請注意,您必須#include <limits.h>才能使用這些常量。

因此,我建議使用以下代碼從用戶那里讀取一個數字並對其進行驗證:

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

int main( void )
{
    char buffer[1024], *p;
    long num_long;
    int  num_int;

    //prompt user for input
    printf( "Enter a number: " );

    //get one line of input from input stream
    if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
    {
        fprintf( stderr, "Unrecoverable error reading from input!\n" );
        exit( EXIT_FAILURE );
    }

    //make sure that entire line was read in (i.e. that
    //the buffer was not too small)
    if ( strchr( buffer, '\n' ) == NULL )
    {
        printf( "Line input was too long!\n" );
        exit( EXIT_FAILURE );
    }

    //attempt to convert string to number
    errno = 0;
    num_long = strtol( buffer, &p, 10 );
    if ( p == buffer )
    {
        printf( "Error converting string to number\n" );
        exit( EXIT_FAILURE );
    }

    //make sure that no range error occurred
    if ( errno == ERANGE || num_long < INT_MIN || num_long > INT_MAX )
    {
        printf( "Range error!\n" );
        exit( EXIT_FAILURE );
    }

    //range is ok, so we can convert to int
    num_int = (int)num_long;

    //make sure that remainder of line contains only whitespace,
    //so that input such as "6sdfh4q" gets rejected
    for ( ; *p != '\0'; p++ )
    {
        if ( !isspace( (unsigned char)*p ) )
        {
            printf( "Unexpected input encountered!\n" );
            exit( EXIT_FAILURE );
        }
    }

    //number was successfully converted, so we print it
    printf( "You entered the following valid number: %d\n", num_int );
}

輸入有效數字時此程序的示例輸出:

Enter a number: 65
You entered the following valid number: 65

輸入無效輸入時此程序的示例輸出:

Enter a number: 6sdfh4q
Unexpected input encountered!

輸入超出范圍的輸入時此程序的示例輸出:

Enter a number: 3000000000
Range error!

將其讀入字符串並檢查長度,然后在字符串上調用atol()sscanf()將其轉換為int。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM