首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >正确使用strtol

正确使用strtol
EN

Stack Overflow用户
提问于 2013-01-06 04:32:39
回答 5查看 36.7K关注 0票数 35

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

代码语言:javascript
运行
复制
./convertToLong 55
Could not convert 55 to long and leftover string is: 55 as long is 55

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

代码:

代码语言:javascript
运行
复制
#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;
}
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-01-06 04:35:34

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

代码语言:javascript
运行
复制
if (*temp != '\0')
票数 24
EN

Stack Overflow用户

发布于 2013-01-06 05:30:33

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

当您第一次遇到strtol()及其相关的错误处理的完整规范时,它非常复杂,令人惊讶地复杂。有一件事是绝对正确的,那就是使用一个函数来调用strtol();在代码中使用它“原始”可能是不正确的。

由于这个问题同时使用了C和C++标记,我将引用C2011标准;您可以在C++标准中找到合适的措辞。

ISO/IEC9899:2011§7.22.1.4 strtolstrtollstrtoulstrtoull函数

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

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

?7如果主题序列为空或不具有预期的形式,则不执行任何转换;如果endptr不是空指针,则nptr的值存储在endptr指向的对象中。

返回

²8 strtolstrtollstrtoulstrtoull函数返回转换后的值(如果有)。如果无法执行任何转换,则返回零。如果正确的值超出可表示值的范围,则返回LONG_MIN、LONG_MAX、LLONG_MIN、LLONG_MAX、ULONG_MAX或ULLONG_MAX (根据值的返回类型和符号(如果有)),并将宏ERange值存储在errno中。

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

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

代码语言:javascript
运行
复制
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终止;如果不是这样,就不能保证它们会及时出现。

现在,在库代码中,您可能不想要任何打印,而您的调用代码可能想知道转换是否成功,因此您可能还需要修改界面。在这种情况下,您可能需要修改函数,使其返回一个成功/失败指示:

代码语言:javascript
运行
复制
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;
}

你可以像这样使用它:

代码语言:javascript
运行
复制
if (parseLong(str, &value))
    …conversion successful…
else
    …handle error…

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

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

代码语言:javascript
运行
复制
int saved = errno;  // At the start, before errno = 0;

…rest of function…

if (errno == 0)     // Before the return
    errno = saved;
票数 66
EN

Stack Overflow用户

发布于 2020-09-27 22:13:10

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

代码语言:javascript
运行
复制
static long parseLong(const char * str) {
    int base = 0;
    char *endptr;
    errno = 0;
    long val = strtol(str, &endptr, base);

3个由标准C库指定/支持的测试:

  1. 是否已完成任何转换?

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

  1. 在范围内吗?

else if (errno == ERANGE) puts(“输入超出长范围”);

  1. 拖尾垃圾?

else if (*endptr) put(“数字文本后的额外垃圾。”);

成功

代码语言:javascript
运行
复制
    else printf("Success %ld\n", val);

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

代码语言:javascript
运行
复制
    else if (errno) puts("Some implementation error found.");

或者与errno == ERANGE测试相结合。

示例简洁的代码,也利用了常见的实现扩展。

代码语言:javascript
运行
复制
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;
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14176123

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档