C语言 strtol的正确用法

vc6uscn9  于 2023-05-28  发布在  其他
关注(0)|答案(5)|浏览(164)

下面的程序将字符串转换为long,但根据我的理解,它也返回一个错误。我依赖的事实是,如果strtol成功地将string转换为long,那么strtol的第二个参数应该等于NULL。当我用55运行下面的应用程序时,我得到以下消息。

./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55

如何成功检测strtol的错误?在我的应用程序中,零是一个有效值。
代码:

#include <stdio.h>
#include <stdlib.h>

static long parseLong(const char * str);

int main(int argc, char ** argv)
{
    printf("%s as long is %ld\n", argv[1], parseLong(argv[1]));
    return 0;
 }

static long parseLong(const char * str)
{
    long _val = 0;
    char * temp;

    _val = strtol(str, &temp, 0);

    if(temp != '\0')
            printf("Could not convert %s to long and leftover string is: %s", str, temp);

    return _val;
}
dfuffjeb

dfuffjeb1#

请注意,以下划线开头的名称是为实现保留的;最好避免在代码中使用此类名称。因此,_val应该是val
strtol()及其相关的错误处理的完整规范非常复杂,当您第一次遇到它时,复杂得令人惊讶。有一件事您做得非常正确,那就是使用一个函数来调用strtol();在代码中使用“原始”可能是不正确的。
由于这个问题同时使用了C和C++,我将引用C2011标准;你可以在C++标准中找到适合自己的措辞。

ISO/IEC 9899:2011 §7.22.1.4 strtolstrtollstrtoulstrtoull函数

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);
¶2 [...]首先,它们将输入字符串分解为三个部分:初始的、可能为空的空白字符序列(由isspace函数指定)、类似于以由base的值确定的某个基数表示的整数的主题序列、以及一个或多个未识别字符的最终字符串,包括输入字符串的终止空字符。[...]
¶7如果主题序列为空或没有预期的形式,则不执行转换; nptr的值存储在endptr所指向的对象中,前提是endptr不是空指针。

返回

¶8 strtolstrtollstrtoulstrtoull函数返回转换后的值。如果无法执行转换,则返回零。如果正确的值超出了可表示值的范围,则返回LONG_MIN、LONG_MAX、LLONG_MIN、LLONG_MAX、ULONG_MAX或ULLONG_MAX(根据返回类型和值的符号,如果有的话),宏ERANGE的值存储在errno中。
请记住,没有标准的C库函数会将errno设置为0。因此,为了可靠,必须在调用strtol()之前将errno设置为零。
所以,你的parseLong()函数可能看起来像:

static long parseLong(const char *str)
{
    errno = 0;
    char *temp;
    long val = strtol(str, &temp, 0);

    if (temp == str || *temp != '\0' ||
        ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
        fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n",
                str, temp);
        // cerr << "Could not convert '" << str << "' to long and leftover string is '"
        //      << temp << "'\n";
    return val;
}

请注意,如果出现错误,将返回0或LONG_MIN或LONG_MAX,具体取决于strtol()返回的内容。如果你的调用代码需要知道转换是否成功,你需要一个不同的函数接口--见下文。另外,请注意,错误应该打印到stderr而不是stdout,并且错误消息应该以换行符\n结束;如果不是,就不能保证它们会及时出现。
现在,在库代码中,您可能不希望任何打印,并且您的调用代码可能想知道转换是否成功,因此您可能也要修改接口。在这种情况下,您可能会修改函数,使其返回成功/失败指示:

bool parseLong(const char *str, long *val)
{
    char *temp;
    bool rc = true;
    errno = 0;
    *val = strtol(str, &temp, 0);

    if (temp == str || *temp != '\0' ||
        ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
        rc = false;

    return rc;
}

你可以使用如下:

if (parseLong(str, &value))
    …conversion successful…
else
    …handle error…

如果你需要区分'trailing junk','invalid numeric string','value too big'和'value too small'(以及'no error'),你应该使用一个整数或enum而不是布尔返回码。如果您希望允许白色但不允许其他字符,或者如果您不希望允许任何前导空格,则在函数中需要做更多的工作。代码允许八进制、十进制和十六进制;如果你想要严格的十进制,你需要在调用strtol()时将0改为10。
如果你的函数要伪装成标准库的一部分,它们不应该永久地将errno设置为0,所以你需要 Package 代码来保留errno

int saved = errno;  // At the start, before errno = 0;

…rest of function…

if (errno == 0)     // Before the return
    errno = saved;
5uzkadbs

5uzkadbs2#

你就快成功了temp本身不会为null,但如果整个字符串被转换,它将指向一个null字符,因此您需要取消引用它:

if (*temp != '\0')
ggazkfy8

ggazkfy83#

如何成功检测strtol中的错误?

static long parseLong(const char * str) {
    int base = 0;
    char *endptr;
    errno = 0;
    long val = strtol(str, &endptr, base);

标准C库指定/支持的3个测试:
1.是否已完成转换?

if (str == endptr) puts("No conversion.");

1.在射程内?

// Best to set errno = 0 before the strtol() call.
 else if (errno == ERANGE) puts("Input out of long range.");

1.跟踪垃圾?

else if (*endptr) puts("Extra junk after the numeric text.");

成功案例

else printf("Success %ld\n", val);

str == NULLbase这样的输入不是0,[2到36]是 undefined behavior。各种实现(C库的扩展)通过errno提供定义的行为和报告。我们可以添加第四个测试。

else if (errno) puts("Some implementation error found.");

或者合并errno == ERANGE测试。
示例简洁代码,也利用了常见的实现扩展。

long my_parseLong(const char *str, int base, bool *success) {
    char *endptr = 0;
    errno = 0;
    long val = strtol(str, &endptr, base);
   
    if (success) {
      *success = endptr != str && errno == 0 && endptr && *endptr == '\0';
    }
    return val;
}
ie3xauqp

ie3xauqp4#

你错过了一个间接的层次。你想检查字符是否是终止符NUL,如果指针是NULL则不检查:

if (*temp != '\0')

顺便说一下,这不是一个很好的错误检查方法。strto*系列函数的正确错误检查方法不是通过比较输出指针和字符串的结尾来完成的。它应该通过检查零返回值并获取errno的返回值来完成。

disho6za

disho6za5#

你应该检查一下

*temp != '\0'

您还应该能够在调用strotol之后检查errno的值,如下所示:

RETURN VALUES
     The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result
     of the conversion, unless the value would underflow or overflow.  If no conver-
     sion could be performed, 0 is returned and the global variable errno is set to
     EINVAL (the last feature is not portable across all platforms).  If an overflow
     or underflow occurs, errno is set to ERANGE and the function return value is
     clamped according to the following table.

       Function       underflow     overflow
       strtol()       LONG_MIN      LONG_MAX
       strtoll()      LLONG_MIN     LLONG_MAX
       strtoimax()    INTMAX_MIN    INTMAX_MAX
       strtoq()       LLONG_MIN     LLONG_MAX

相关问题