正确使用步道

Correct usage of strtol

本文关键字:      更新时间:2023-10-16

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

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

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

代码:

#include <stdio.h>
#include <stdlib.h>
static long parseLong(const char * str);
int main(int argc, char ** argv)
{
    printf("%s as long is %ldn", 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 != '')
            printf("Could not convert %s to long and leftover string is: %s", str, temp);
    return _val;
}

注意,以下划线开头的名称是为实现保留的;最好避免在代码中使用这样的名称。因此,_val应该只是val

strtol()及其相关的错误处理的完整规范是复杂的,当你第一次看到它时,会惊讶地发现它是复杂的。有一件事你做的绝对正确的是使用一个函数来调用strtol();在代码中使用"raw"可能是不正确的。

由于问题标记为C和c++,我将引用C2011标准;你可以自己在c++标准中找到合适的措辞。

ISO/IEC 9899:2011§7.22.1.4 strtol, strtoll, strtoulstrtoull功能

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);

¶2[…)首先,它们将输入字符串分解为三部分:一个初始的(可能为空的)序列空白字符(由isspace函数指定),一个主题序列类似于用基数和a的值确定的基数表示的整数一个或多个不可识别字符的最后字符串,包括结束的null输入字符串的字符。[…]

如果主题序列为空或没有期望的形式,则不进行转换执行;nptr的值存储在endptr所指向的对象中说明endptr不是空指针。

返回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 != '' ||
        ((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 != '' ||
        ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE))
        rc = false;
    return rc;
}

你可以这样使用:

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

如果您需要区分'尾随垃圾','无效数字字符串','值太大'和'值太小'(和'无错误'),您将使用整数或enum而不是布尔返回码。如果您希望允许尾随空格但不允许其他字符,或者如果您不希望允许任何前导空格,则在函数中需要做更多的工作。代码允许八进制、十进制和十六进制;如果您想要严格的十进制,则需要在调用strtol()时将0更改为10。

如果你的函数要伪装成标准库的一部分,它们不应该永久地将errno设置为0,所以你需要包装代码以保留errno:

int saved = errno;  // At the start, before errno = 0;
…rest of function…
if (errno == 0)     // Before the return
    errno = saved;

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

if (*temp != '')

我如何成功地从漫游中检测错误?

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.");
    
  2. 在范围
  3. 吗?

     // Best to set errno = 0 before the strtol() call.
     else if (errno == ERANGE) puts("Input out of long range.");
    
  4. 尾矿垃圾吗?

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

成功
    else printf("Success %ldn", val);

输入str == NULLbase不为0,[2 ~ 36]为未定义行为。各种实现(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 == '';
    }
    return val;
}

你错过了一个间接的层次。您需要检查字符是否是终止NUL,而不是指针是否为NULL:

if (*temp != '')
顺便说一下,这不是一种检查错误的好方法。strto*系列函数的正确错误检查方法不是通过比较输出指针与字符串的末尾来完成的。它应该通过检查零返回值并获得errno的返回值来完成

您应该检查

*temp != ''

您还应该能够在调用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
相关文章:
  • 没有找到相关文章