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

8
根据strtoul的文档,该函数返回转换后的整数值作为长整型。如果无法进行有效的转换,则返回零值。
如果我正在解析一个用户提供的字符串"0",而对于我的应用程序来说,"0"可能是一个有效的输入,那么在这种情况下,似乎我无法从使用strtoul中确定是否执行了有效的转换。有没有其他方法来处理这个问题?

3
如果endptr指向开头,你可以检查一下吗? - Osiris
1
可能还有其他无效的字符串,比如asd123。您还可以检查字符串的第一个字符是否为数字(如果字符串不为空)。总的来说,Sourav Ghosh的答案似乎更全面。 - nm_tp
4个回答

9
请继续阅读man页面

由于strtoul()在成功和失败时都可以合法地返回0ULONG_MAX(对于strtoull()ULLONG_MAX),所以调用程序在调用前应将errno设置为0,然后通过检查调用后errno是否具有非零值来确定是否发生了错误。

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


4
如何使用strtoul解析可能包含0的字符串?
strtoul()返回的任何值都可能来自于预期的字符串输入或者来自于其他不那么预期的字符串。因此需要进行进一步的测试。
下面这些字符串都会使strtoul()返回0:
- "0"、"-0"、"+0" - ""、"abc" - 通常被认为是OK的:" 0" - 根据目标而定,可能是OK或者不OK的:"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)。在粗略的文档审查中,经常忽略此问题。

  • 允许前导空格。这可能是需要的,也可能不是。


为了还要检测负值:

// 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");

1

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

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

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

1
考虑以下函数:
#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中的无符号长整型,并返回指向未解析字符的指针,同时将无符号长整型保存到*to中。如果出现错误,则函数将返回NULL,并设置errno以指示错误。
如果将该函数与man 3 strtoul进行比较,您会发现它正确处理了所有错误情况,并且仅在src产生有效的无符号长整型时才返回非NULL值。特别注意注释部分,还要注意如何处理负数。
对于strtol()strtod()strtoull(),都适用这种模式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接