我在c中编写一个程序,它在命令行中接受来自用户的参数,但该参数必须是有效的unsigned int。例如,如果用户输入值-1,那么我将不得不打印错误代码。或者,如果用户输入的内容高于4294967295,那么我还会打印错误代码。
我不确定如何检查他们的输入是否在正确的范围内(0和4294967295)。
它可以实现如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>
int main(int argc, char** argv)
{
long long num;
char * pEnd;
if(argc>=2)
{
num = strtoll(argv[1], &pEnd, 10);
if(pEnd==argv[1]||*pEnd!='\0'||num<0||num>UINT_MAX)
{
// printf("Error\n");
//Generate error message
}
else
{
// printf("Success\n");
}
}
}
使用strtoll()将字符串转换为整数。 strtoll()
可用于检查输入是否为数字。然后检查号码在[0,4294967295]范围内。
编辑
正如Luis Colorado 评论
让我们假设我们对unsigned long long整数有同样的问题。那么我们如何解决问题呢?
为了解决这个问题,这种方法可以用于字符串比较。这个版本为-0
和0
的成功生成错误,并没有在那里处理,因为OP没有澄清这种情况。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>
#include<ctype.h>
void trimWhiteSpace(char *ch)
{
int j=0, i=0;
while(isspace((unsigned char)ch[i])) i++;
while(isdigit(ch[i])) ch[j++]=ch[i++];
ch[j]='\0';
}
int main(int argc, char** argv)
{
unsigned long long num;
char * pEnd;
const int max_len = snprintf(NULL, 0, "%llu", ULONG_LONG_MAX);
char max_num[max_len+1];
snprintf(max_num, max_len+1, "%llu", ULONG_LONG_MAX);
if(argc>=2)
{
trimWhiteSpace(argv[1]);
int len = strlen(argv[1]);
num = strtoll(argv[1], &pEnd, 10);
if(len==0||*pEnd!='\0'||len>max_len||(len==max_len&&strcmp(argv[1], max_num)==1)||argv[1][0]=='-')
{
printf("Error\n");
//Generate error message
}
else
{
//Success
printf("Success: %llu\n", strtoull(argv[1], NULL, 10));
}
}
}
验证用户输入号实际上是c中的有效unsigned int
strtoul()
是正确使用的功能。它接受'-'
会带来一些问题。让我们假设前导空格是正常的,并手动检查字符串的前端。
// 0: invalid, 1: valid
int unsigned_int_valid(const char *s) {
while (isspace((unsigned char) *s) s++;
// Fail on '-' as any negative number is out of range.
if (*s == '-') return 0; // Could add code to allow "-0"
if (*s == '+') s++;
if (!isdigit((unsigned char) *s) return 0;
// Code knowns string begins with a digit
errno = 0; // Clear this global value as code tests it later.
char *endptr; // Pointer where parsing stopped.
unsigned long ul = strtoul(s, &endptr, 0);
// Usually this test is needed to see if _any_ conversion happened,
// but code knowns the first character is a digit.
#if 0
if (s == endptr) return 0;
#endif
// Could march down the end of the string allowing trailing white-space
while (isspace((unsigned char) *endptr) endptr++;
// Extra text after the digits?
if (*endptr) return 0;
// Overflow? strtoul sets `errno = ERANGE` when "outside the range" of unsigned long
if (errno) return 0;
#if ULONG_MAX > UINT_MAX
// Does 'ul` value exceeds `unsigned` range?
if (ul > UINT_MAX) return 0;
#endif
return 1;
}
您可以尝试下面的步骤组合
正则表达式匹配
/* Compile regular expression */
reti = regcomp(®ex, "^[[:digit:]]", 0);
if( reti ){ fprintf(stderr, "Could not compile regex\n"); exit(1); }
/* Execute regular expression */
reti = regexec(®ex, argv[1], 0, NULL, 0);
if( !reti ){
puts("Match");
}
else if( reti == REG_NOMATCH ){
puts("No match");
}
else{
regerror(reti, ®ex, msgbuf, sizeof(msgbuf));
fprintf(stderr, "Regex match failed: %s\n", msgbuf);
exit(1);
}
然后转换为整数
strtoul()
其次是限制max或min
这确实是unsigned
类型要解决的一个难题,因为你不能测试范围(因为在unsigned
中没有极值之上或之下的值 - 每个数字都在极端之间)
一种方法可能是在解析时检查溢出,因为输入溢出导致数字低于最大可能值。您可以读取数字,每个字符为char,并在溢出时标记错误(当您检测到添加数字不会产生更大的数字时),如下所示:
const char *input_string = <<the input string goes here>>;
const char *s;
unsigned res = 0;
const unsigned base = 10; /* numbering base */
#if INCLUDE_SPACES_BEFORE_NUMBER
while (isspace(*s)) s++; /* skip spaces. Optional */
#endif
for (s = input_string; *s; *s++) {
const unsigned old_res = res;
if (!isdigit(*s)) {
/* flag_error(non_digits_in_string); */
SIGNAL_SOME_INVALID_RESULT_TO_FLAG_NONDIGIT_AND_RETURN();
/*NOTREACHED*/
}
res *= base;
res += *s - '0'; /* digits are warranteed to be consecutive by std */
if (res < old_res) {
/* AN OVERFLOW HAS OCCURED */
SIGNAL_SOME_INVALID_RESULT_TO_FLAG_OVERFLOW_AND_RETURN();
/*NOTREACHED*/
}
}
return res;
但是这种方法是无效的,因为unsigned
溢出是未定义的行为,所以我们必须在让它发生之前检查它。
下一种方法可能是考虑使用正则表达式来匹配有效的无条件范围的字符串。正如您在问题中指出的那样,您的无符号范围从0
到4294967295
,并且可以与以下正则表达式匹配:
^(0|[1-9][0-9]{0,8}|[1-3][0-9]{9}|4[01][0-9]{8}|42[0-8][0-9]{7}|429[0-3][0-9]{6}|4294[0-8][0-9]{5}|42949[0-5][0-9]{4}|429496[0-6][0-9]{3}|4294967[0-1][0-9]{2}|42949672[0-8][0-9]|429496729[0-5])$
如果不匹配,则该数字在您请求的范围内无效或不存在。我已经包含了完整的正则表达式,以显示其上的模式,因此您可以轻松地将其适应其他范围。这样,你只接受有效数字序列的整个空间(我没有包含前导零,因为如果你想要遵循C语法,它们会将数字标记为八进制,但如果你想要它们,只需在初始锚点之间插入0*
^
和下一个模式(...
,如果你想让它们在数字前面,那么空格/制表符也一样。
标准库函数strtoul
可用于此目的,如下所示:
#include <stdlib.h>
#include <limits.h>
char *numstr = argv[1], *endptr;
unsigned long result; /* where we save our number to */
/* skip leading whitespace */
while (isblank(*numstr))
numstr++;
errno = 0;
result = strtoul(numstr, &endptr, 10);
if (numstr[0] == '\0' || endptr[0] != '\0') {
/* this branch taken if input is not a number */
else if (errno == ERANGE || result > UINT_MAX || numstr[0] == '-') {
/* this branch taken if number is out of range */
} else {
/* this branch taken on successful conversion */
}
鉴于strtoul()
的细微差别,代码可以简单地为有效的unsigned
滚动自己的字符串检查。
(white-space)*(+-)[0-9](0-9)*(white-space)*
这个通过"-0"
。失败其他负数。
失败超出范围。
允许前导和尾随空格。
允许无结尾前导零:“000000000000000000000000000000001”。
// 0: OK
// 1: overflow
// 2: invalid character sequence
int check_unsigned(const char *s) {
// consume leading white space
while (isspace((unsigned char) *s)) {
s++;
}
// get sign
int sign = *s;
if (sign == '+' || sign == '-') s++;
// convert
unsigned sum = 0;
const char *first_digit = s;
while (isdigit((unsigned char) *s)) {
unsigned sum_before = sum;
sum = sum * 10 + *s - '0';
if (sum < sum_before) {
return 1; // overflow
}
s++;
}
// Test for < 0
if (sign == '-' && sum > 0) {
return 1;
}
// Test for no digits
if (s == first_digit) {
return 2; // no digits
}
// Test for trailing white space
while (isspace((unsigned char) *s)) {
s++;
}
if (*s != '\0') {
return 2;
}
return 0;
}
我想我们可以用一种简单的方式实现这个功能,
If((variable_name>0)&&(variable_name<4294967295))
printf("entered number is a valid unsigned int one");
else
printf("entered value is not valid");
上面的代码解决了你的要求,但我不确定它的时间复杂性,因为它可能发生了大量的比较。