良好的C编码练习/礼仪用于完整性检查

时间:2016-02-20 12:37:08

标签: c design-patterns error-handling etiquette

两部分问题;

我来自高级语言,所以这是关于形式不是功能的问题;

我编写了一个带有char []的isnumeric()函数,如果字符串是一个利用ctype中的isdigit()函数的数字,则返回1。类似的函数内置于其他语言,我总是使用类似的函数来检查数据,然后再将其转换为数字类型。主要是因为如果尝试将非数字字符串转换为整数,某些语言转换函数会严重失败。

但它似乎是一个必须完成所有循环以补偿C中缺少字符串的kludge,这构成了问题的第一部分;

在调用atoi()之前,在C中捕获来自atoi()的0返回以代替对数据进行完整性检查是否可以接受? atoi()(以及其他ascii到xx函数)的工作方式似乎很适合完全消除完整性检查。跳过支票肯定会更有效率。

问题的第二部分是; 是否有C函数或公共库函数用于数字完整性检查 在一个字符串? (通过字符串,我当然是指char [])

5 个答案:

答案 0 :(得分:1)

  

在调用atoi()之前,在C中捕获来自atoi()的0返回代替对数据进行完整性检查是否可以接受?

永远不会陷入错误,除非错误表明如果代码中没有错误就不会发生编程错误。如果出现错误,请务必返回某种错误结果。查看OpenBSD strtonum函数,了解如何设计这样的接口。

  

问题的第二部分是;是否有C函数或公共库函数用于字符串的数字完整性检查? (通过字符串,我当然是指char [])

永远不要使用atoi,除非您编写的程序没有错误检查,因为atoi没有进行任何错误检查。 strtol系列函数允许您检查错误。这是一个如何使用它们的简单示例:

int check_is_number(const char *buf)
{
    const char *endptr;
    int errsave = errno, errval;
    long result;

    errno = 0;
    result = strtol(buf, &endptr, 0);
    errval = errno;
    errno = errsave;

    if (errval != 0)
        return 0; /* an error occured */

    if (buf[0] == '\0' || *endptr != '\0')
        return 0; /* not a number */

    return 1;
}

请参阅之前链接的手册页,了解strtol(基础)的第三个参数如何影响它的作用。

如果值超出所需类型的范围(例如errno),则

ERANGE设置为long。在这种情况下,返回值为LONG_MAXLONG_MIN

答案 1 :(得分:1)

如果转换方法返回错误指示(与发生错误的香蕉不同,或者没有提供确定是否发生错误的确定方法),那么实际上不需要检查字符串是否为数字试图转换它。

考虑到这一点,如果您需要检查转换错误,使用atoi()并不是一个特别好的功能。零输入将返回零,以及错误,并且无法检查原因。使用的更好的函数是(假设您想要读取整数值)是strtol()。虽然strtol()在整数上返回零,但它也返回可用于检查失败的信息。例如;

long x; 
char *end;

x = strtol(your_string, &end, 10);
if (end == your_string)
{
     /*  nothing was read due to invalid character or the first 
         character marked the end of string */
}
else if (*end != '\0`)
{
    /*  an integral value was read, but there is following non-numeric data  */
}

其次,使用strtol()有其他选择,虽然涉及更多开销。可以检查sscanf()(实际上,scanf()系列中的所有函数)的返回值是否存在错误情况。

没有标准函数来检查字符串是否为数字,但可以使用上面的内容轻松滚动。

int IsNumeric(char *your_string)
{
     /*  This has undefined behaviour if your_string is not a C-style string
           It also deems that a string like "123AB" is non-numeric
     */
     long x; 
     char *end;

     x = strtol(your_string, &end, 10);
     return !(end == your_string || *end != '\0`);
}

上述任何选项中都没有(显式)循环。

答案 2 :(得分:1)

  

在调用atoi()之前,在C中捕获来自atoi()的0返回以代替对数据进行完整性检查是否可以接受?

没有。 @FUZxxl很好地回答了这个问题。

  

对字符串进行数字完整性检查是否有C函数或公共库函数?

在C中,将字符串转换为数字并检查转换是否有效通常是一起完成的。使用的功能取决于所寻求的号码类型。 " 1.23"对浮点类型有意义,但不是整数。

// No error handle functions
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
double atof(const char *nptr);

// Some error detection functions
if (sscanf(buffer, "%d", &some_int) == 1) ...
if (sscanf(buffer, "%lf", &some_double) == 1) ...

// Robust methods use
long               strtol(   const char *nptr, char ** endptr, int base);
long long          strtoll(  const char *nptr, char ** endptr, int base);
unsigned long      strtoul(  const char *nptr, char ** endptr, int base);
unsigned long long strtoull( const char *nptr, char ** endptr, int base);
intmax_t           strtoimax(const char *nptr, char ** endptr, int base);
uintmax_t          strtoumax(const char *nptr, char ** endptr, int base);
float              strtof(   const char *nptr, char ** endptr);
double             strtod(   const char *nptr, char ** endptr);
long double        strtold(  const char *nptr, char ** endptr);

这些强大的方法使用char ** endptr来存储扫描停止的字符串位置。如果找到数字数据,则*endptr == nptr。所以常见的测试可能是

char *endptr;
y = strto...(buffer, ..., &endptr);
if (buffer == endptr) puts("No conversion");
if (*endptr != '\0') puts("Extra text");

如果范围超过这些函数,则都设置全局变量errno = ERANGE;并返回该类型的最小值或最大值。

errno = 0;
double y = strtod("1.23e10000000", &endptr);
if (errno == ERANGE) puts("Range exceeded");

整数函数允许从基数2到36进行基数选择。如果使用0,则字符串"0x""0X""0",{的前导部分{1}} - >基数16,16,8,10。

other

阅读规范或帮助页面了解更多详情。

答案 3 :(得分:0)

您可能不需要函数来检查字符串是否为数字。您很可能需要将字符串转换为数字,以便这样做。然后检查转换是否成功。

long number;
char *end;
number = strtol(string, &end, 10);
if ((*string == '\0') || (*end != '\0'))
{
    // empty string or invalid number
}

strtol的第二个参数用于指示解析结束的位置(第一个非数字字符)。如果我们到达字符串的末尾,那个字符将是\0。如果您想在号码后面允许其他字符(例如),您可以使用switch进行检查。

strtol适用于long个整数。如果您需要其他类型,请参阅手册页:man 3 strtol。对于浮点数,您可以使用strtod

如果程序逻辑允许字符串不是数字(例如,如果它来自用户或文件),请不要陷阱。

答案 4 :(得分:0)

OP稍后commneted

  

我正在寻找一种方法来确定该字符串是仅包含10位基数还是小数或逗号。所以如果字符串是100,000.01我想要从func获得正回报。字符串中任何位置的任何其他ascii字符都会导致负的返回值。

如果您有兴趣,请使用;

if (buffer[strspn(buffer, "0123456789.,")] == '\0') return 0; // Success
else return -1; // Failure