【问题标题】:Good C Coding practice/etiquette for integrity checks良好的 C 编码实践/完整性检查礼仪
【发布时间】:2023-04-08 20:22:02
【问题描述】:

两部分问题;

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

我编写了一个 isnumeric() 函数,它接受一个 char[] 并在字符串是数字时返回 1,这利用了 ctype 中的 isdigit() 函数。其他语言内置了类似的功能,我一直使用类似的功能在将数据转换为数字类型之前对其进行完整性检查。主要是因为如果您尝试将非数字字符串转换为整数,某些语言转换函数会失败。

但似乎不得不进行所有循环以弥补 C 中缺少字符串的问题,这是问题的第一部分;

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

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

【问题讨论】:

  • I've witten an isnumeric() function.is 开头的标识符被保留。良好的编码习惯意味着您避免使用它们。
  • 由于多种原因跑题了。
  • 你关心数字字符串是否可以用 int 表示吗?还是只想知道字符串是否只包含十进制数字?
  • 关闭选民:第二个问题“是否有用于对字符串进行数字完整性检查的 C 函数或通用库函数?”肯定不是意见基础:strtol() strtod(), strtoull() .... (8+ functions)
  • @jxh 我正在寻找一种方法来确定字符串是否仅包含基数 10 位数字或小数点或逗号。因此,如果字符串为 100,000.01,我希望 func 获得正回报。字符串中任何位置的任何其他 ascii 字符都将导致负返回值。

标签: c design-patterns error-handling


【解决方案1】:

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

永远不要陷入错误,除非错误表明如果代码中没有错误就不会发生编程错误。在发生错误时总是返回某种错误结果。查看 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

【讨论】:

  • 我认为,值得一提的是,该函数在 0xabc 上也返回 true,在 0123456789 上返回 false(以及为什么会这样)。也许10 作为strtol() 的第三个参数更符合OP 的期望。
  • @Ctx 我希望 OP 阅读我链接到的手册页。但让我对此事发表评论。
  • 很好地保留了errno,这是一个微妙有用的代码添加。 (IMO:也应该在if (errno != 0) return 0;之前恢复)
  • @FUZxxl - 感谢您提供的出色示例!我一直在玩 strtol() 和 strtof()。我试图找到一种干净的方法来检查单个调用中的任一数字类型,而不必尝试这个 - 如果失败 - 试试那个,这就是为什么我求助于循环遍历每个元素并使用 '!isdigit(char[i ]) && char[i] != '.''
  • @JackGalt 该方法不会捕获过低。我的代码有一个小问题,请给我一点时间来应用 chux 的修复。
【解决方案2】:

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

考虑到这一点,如果您需要检查转换错误,使用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`);
}

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

【讨论】:

    【解决方案3】:

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

    没有。 @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"other --> 基数为 16, 16、8、10。

    long y = strtol(buffer, &endptr, 10);
    

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

    【讨论】:

      【解决方案4】:

      您可能不需要函数来检查字符串是否为数字。您很可能需要将字符串转换为数字,因此请执行此操作。然后检查是否转换成功。

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

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

      strtollong 整数一起使用。如果您需要其他类型,您应该查阅手册页:man 3 strtol。对于浮点数,您可以使用strtod

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

      【讨论】:

        【解决方案5】:

        稍后操作commneted:

        我正在寻找一种方法来确定字符串是否仅包含 10 位数字或小数或逗号。因此,如果字符串是 100,000.01,我希望从 func 获得正回报。字符串中任何位置的任何其他 ascii 字符都将导致返回负值。

        如果是你的全部兴趣,请使用;

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

        【讨论】:

          猜你喜欢
          • 2013-09-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多