如何使用 `strtoul` 解析零可能有效的字符串?

How to use `strtoul` to parse string where zero may be valid?

根据 strtoul 的文档,关于其 return 值...

This function returns the converted integral number as a long int value. If no valid conversion could be performed, a zero value is returned.

如果我正在解析用户提供的字符串“0”,对于我的应用程序,“0”可能是一个有效条目怎么办?在那种情况下,我似乎无法通过使用 strtoul 来确定是否执行了有效转换。还有其他方法可以解决这个问题吗?

进一步阅读man page

Since strtoul() can legitimately return 0 or ULONG_MAX (ULLONG_MAX for strtoull()) on both success and failure, the calling program should set errno to 0 before the call, and then determine if an error occurred by checking whether errno has a nonzero value after the call.

此外,要处理另一种情况,即在输入中未读取任何数字。如果发生这种情况,strtol() 会将 *endptr 的值设置为 nptr 的值。因此,您还应该检查指针值比较是否相等。

一种解决方案是传递 char 指针的地址并检查它是否指向字符串的开头:

char *str = "0";
char *endptr;
unsgined long x = strtoul(str, &endptr, 10);

if(endptr == str)
{
    //Nothing was read
}

How to use strtoul to parse string where zero may be valid?

strtoul() 编辑的任何值 return 可能来自预期的字符串输入或来自 other 不是预期的字符串。进一步的测试是有用的。

以下全部return0来自strtoul()

  • 好的 "0", "-0", "+0"
  • 不正常"""abc"
  • 通常认为可以:" 0"
  • 是否可行取决于目标:"0xyz""0 ""0.0"

strtoul()有多种检测模式。

int base = 10;
char *endptr;  //  Store the location where conversion stopped

errno = 0;
unsigned long y = strtoul(s, &endptr, base);

if (s == endptr) puts("No conversion");      // "", "abc"
else if (errno == ERANGE) puts("Overflow");
else if (*endptr) puts("Extra text after the number"); // "0xyz", "0 ", "0.0"
else puts("Mostly successful");

尚未检测到什么。

  • 负输入。 strtoul() 有效环绕 strtoul("-1", 0, 10) == ULONG_MAX)这个问题在草率的文档审查中经常被遗漏。

  • 前导白色 space 允许。这可能需要也可能不需要。


同时检测负值:

// find sign
while (isspace((unsigned char) *s)) {
  s++;
}
char sign = *s;

int base = 10;
char *endptr;  //  Store the location where conversion stopped
errno = 0;
unsigned long y = strtoul(s, &endptr, base);

if (s == endptr) puts("No conversiosn");
else if (errno == ERANGE) puts("Overflow");
else if (*endptr) puts("Extra text after the number"); 
else if (sign == '-' && y != 0) puts("Negative value"); 
else puts("Successful");

考虑以下函数:

#include <stdlib.h>
#include <errno.h>
/* SPDX-Identifier: CC0-1.0 */

const char *parse_ulong(const char *src, unsigned long *to)
{
    const char    *end;
    unsigned long  val;

    if (!src) {
        errno = EINVAL;
        return NULL;
    }

    end = src;
    errno = 0;
    val = strtoul(src, (char **)(&end), 0);
    if (errno)
        return NULL;
    if (end == src) {
        errno = EINVAL;
        return NULL;
    }

    if (to)
       *to = val;

    return end;
}

该函数解析字符串src中的unsigned long,return指向src中第一个未解析字符的指针,并将unsigned long保存到*to.如果出现错误,该函数将 return NULL 并设置 errno 以指示错误。

如果将该函数与 man 3 strtoul 进行比较,您会发现它可以正确处理所有错误情况,并且当 src 产生有效的 unsigned long 时只有 returns 非 NULL .尤其是看注释部分。还要注意负数是如何处理的。

同样的模式适用于 strtol()strtod()strtoull()