正确使用strtol

时间:2013-01-05 20:32:39

标签: c++ c

下面的程序将字符串转换为long,但根据我的理解,它也会返回错误。我依赖的事实是,如果strtol成功将字符串转换为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;
}

5 个答案:

答案 0 :(得分:46)

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

strtol()及其亲属的错误处理的完整规范是复杂的,当您第一次遇到它时非常复杂。你正在做的一件事是使用函数来调用strtol();在代码中使用'raw'可能不正确。

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

  

ISO / IEC 9899:2011§7.22.1.4strtolstrtollstrtoulstrtoull函数

     

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

     

¶2[...]首先,   他们将输入字符串分解为三个部分:一个初始的,可能是空的序列   空白字符(由isspace函数指定),主题序列   类似于由base的值确定的某个基数表示的整数,和a   一个或多个无法识别的字符的最终字符串,包括终止null   输入字符串的字符。 [...]

     

¶7如果主题序列为空或者没有预期的形式,则不进行转换   执行; nptr的值存储在endptr指向的对象中,提供   <{1}}不是空指针。

     

返回

     

¶8endptrstrtolstrtollstrtoul函数返回转换后的内容   价值,如果有的话。如果无法执行转换,则返回零。如果值正确   超出可表示值的范围,LONG_MIN,LONG_MAX,LLONG_MIN,   返回LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据返回类型   和值的符号,如果有的话),宏ERANGE的值存储在strtoull

请记住,没有标准C库函数将errno设置为0.因此,为了可靠,您必须在调用errno之前将errno设置为零。

因此,您的strtol()函数可能如下所示:

parseLong()

请注意,出错时,返回0或LONG_MIN或LONG_MAX,具体取决于返回的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; } 。如果您的调用代码需要知道转换是否成功,则需要一个不同的功能接口 - 见下文。另请注意,错误应打印到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;
}

如果您需要区分“尾随垃圾”,“无效数字字符串”,“值太大”和“值太小”(以及“无错误”),您将使用整数或{{1}而不是布尔返回码。如果要允许尾随空格而不允许其他字符,或者如果您不想允许任何前导空格,则需要在该函数中执行更多工作。代码允许八进制,十进制和十六进制;如果您想要严格小数,则需要在调用if (parseLong(str, &value)) …conversion successful… else …handle error… 时将0更改为10。

如果您的函数要伪装成标准库的一部分,则不应将enum永久设置为strtol(),因此您需要包装代码以保留errno

0

答案 1 :(得分:15)

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

if (*temp != '\0')

答案 2 :(得分:2)

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

if (*temp != '\0')

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

答案 3 :(得分:1)

你应该检查

*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.

答案 4 :(得分:0)

如何成功检测到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.");
    
  2. 在范围内?

     else if (errno == ERANGE) puts("Input out of long range.");
    
  3. 尾巴是垃圾吗?

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

成功

    else printf("Success %ld\n", 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 == '\0';
    }
    return val;
}