关于C ++:正确使用strtol

Correct usage of strtol

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

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

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

码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

static long parseLong(const char * str);

int main(int argc, char ** argv)
{
    printf("%s as long is %ld
"
, 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;
}


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

第一次运行strtol()及其关联的错误处理的完整规范非常复杂,令人惊讶的复杂。您做的绝对正确的一件事是使用函数调用strtol()。在代码中"原始"使用它可能是不正确的。

由于该问题同时带有C和C ++标签,因此我将引用C2011标准。您可以在C ++标准中找到适合自己的措辞。

ISO/IEC 9899:2011 §7.22.1.4 The strtol, strtoll, strtoul and strtoull functions

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

?2 [...] First,
they decompose the input string into three parts: an initial, possibly empty, sequence of
white-space characters (as specified by the isspace function), a subject sequence
resembling an integer represented in some radix determined by the value of base, and a
final string of one or more unrecognized characters, including the terminating null
character of the input string. [...]

?7 If the subject sequence is empty or does not have the expected form, no conversion is
performed; the value of nptr is stored in the object pointed to by endptr, provided
that endptr is not a null pointer.

Returns

?8 The strtol, strtoll, strtoul, and strtoull functions return the converted
value, if any. If no conversion could be performed, zero is returned. If the correct value
is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN,
LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type
and sign of the value, if any), and the value of the macro ERANGE is stored in errno.

请记住,没有标准的C库函数曾经将errno设置为0。因此,为了可靠起见,必须在调用strtol()之前将errno设置为零。

因此,您的parseLong()函数可能类似于:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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'
"
,
                str, temp);
        // cerr <<"Could not convert '" << str <<"' to long and leftover string is '"
        //      << temp <<"'
";
    return val;
}

请注意,发生错误时,这将返回0或LONG_MIN或LONG_MAX,这取决于返回的strtol()。如果您的调用代码需要知道转换是否成功,则需要一个不同的函数接口-参见下文。另外,请注意,错误应打印到stderr而不是stdout,并且错误消息应以换行符
终止;如果不是,则不能保证它们及时出现。

现在,在库代码中,您可能不想打印任何内容,并且您的调用代码可能想知道转换是否成功,因此您也可以修改接口。在这种情况下,您可能需要修改函数,以便它返回成功/失败指示:

1
2
3
4
5
6
7
8
9
10
11
12
13
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
2
3
4
if (parseLong(str, &value))
    …conversion successful…
else
    …handle error…

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

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

1
2
3
4
5
6
int saved = errno;  // At the start, before errno = 0;

…rest of function…

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


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

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


您缺少间接级别。您要检查字符是否为终止符NUL,而不是指针是否为NULL

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

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


你应该检查一下

1
*temp != '\0'

您还应该能够根据以下要求在调用strotol后检查errno的值:

1
2
3
4
5
6
7
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.