首页 文章

strtol没有改变errno

提问于
浏览
1

我正在研究一个程序,该程序在给定char数组的情况下执行计算,该数组表示格式 HH:MM:SS 的时间 . 它必须解析各个时间单位 . 这是我的代码的缩减版本,只关注时间:

unsigned long parseTime(const char *time)
{
    int base = 10;                    //base 10
    long hours = 60;                  //defaults to something out of range
    char localTime[BUFSIZ]            //declares a local array
    strncpy(localTime, time, BUFSIZ); //copies parameter array to local
    errno = 0;                        //sets errno to 0

    char *par;                        //pointer
    par = strchr(localTime, ':');     //parses to the nearest ':'
    localTime[par - localTime] = '\0';  //sets the ':' to null character

    hours = strtol(localTime, &par, base); //updates hours to parsed numbers in the char array
    printf("errno is: %d\n", errno);       //checks errno
    errno = 0;                             //resets errno to 0
    par++;                                 //moves pointer past the null character
}

问题是如果输入无效(例如 aa:13:13 ), strtol() 显然没有 errno 没有更新到 1 ,所以我不能进行错误处理 . 我错了什么?

4 回答

  • 1

    sscanf() 的一个有用技巧是代码可以执行多次传递来检测错误输入:

    // HH:MM:SS
    int parseTime(const char *hms, unsigned long *secs) {
      int n = 0;
      // Check for valid text
      sscanf(hms "%*[0-2]%*[0-9]:%*[0-5]%*[0-9]:%*[0-5]%*[0-9]%n", &n);
      if (n == 0) return -1; // fail
    
      // Scan and convert to integers
      unsigned h,m,s;
      sscanf(hms "%u:%u:%u", &h, &m, &s);
      // Range checks as needed
      if (h >= 24 || m >= 60 || s >= 60) return -1;
    
      *sec = (h*60 + m)*60L + s;
      return 0;
    }
    
  • 0

    正如其他人所解释的那样,如果无法执行任何转换, strtol 可能无法更新 errno . 如果转换后的值不适合 long 整数,则C标准仅将 errnor 设置为 ERANGE .

    您的代码还有其他问题:

    • 使用 strncpy 复制字符串不正确:如果源字符串长于 BUFSIZlocalTime 将不会以空值终止 . 避免 strncpy ,一个几乎从不适合这个目的的功能很难理解 .

    • 在这种情况下,您无需清除 :'\0'strtol 将停在第一个非数字字符处 . localTime[par - localTime] = '\0'; 是一种复杂的写法 *par = '\0';

    一个更简单的版本是这样的:

    long parseTime(const char *time) {
        char *par;
        long hours;
    
        if (!isdigit((unsigned char)*time) {
            /* invalid format */
            return -1;
        }
        errno = 0;
        hours = strtol(time, &par, 10);
        if (errno != 0) {
            /* overflow */
            return -2;
        }
        /* you may want to check that hour is within a decent range... */
        if (*par != ':') {
            /* invalid format */
            return -3;
        }
        par++;
        /* now you can parse further fields... */
        return hours;
    }
    

    我将返回类型更改为 long ,以便您可以轻松检查无效格式,甚至可以确定负返回值中的哪个错误 .

    要获得更简单的替代方案,请使用 sscanf

    long parseTime(const char *time) {
        unsigned int hours, minutes, seconds;
        char c;
    
        if (sscanf(time, "%u:%u:%u%c", &hours, &minutes, &seconds, &c) != 3) {
            /* invalid format */
            return -1;
        }
        if (hours > 1000 || minutes > 59 || seconds > 59) {
            /* invalid values */
            return -2;
        }
        return hours * 3600L + minutes * 60 + seconds;
    }
    

    此方法仍然接受不正确的字符串,例如 1: 1: 112:00000002:1 . 手工解析字符串似乎是最简洁有效的解决方案 .

  • 1

    如果不能执行转换,则不需要 strtol 来生成错误代码 . 相反,您应该使用第二个参数来存储转换后的最终位置,并将其与初始位置进行比较 .

    顺便说一下,您的代码中还有许多其他错误,这些错误不会影响您所看到的问题,但也应该修复,例如错误使用 strncpy .

  • 0

    hours = strtol(localTime, &par, base); 语句之后,您必须先保存errno的值 . 因为在此语句之后,您将调用 printf() 语句,该语句也相应地设置 errno .

    printf("errno is: %d\n", errno);
    

    所以在这个语句中"errno"给出 printf() 的错误指示而不是 strtol() ...这样做在调用任何库函数之前保存"errno",因为大多数库函数都与"errno"交互 . 正确的用途是:

    hours = strtol(localTime, &par, base);
    int saved_error = errno;       // Saving the error...
    printf("errno is: %d\n", saved_error);
    

    现在检查一下 . 它肯定会给出正确的输出......还有一件事要将此 errno 转换为一些有意义的字符串来表示错误使用 strerror() 函数为:

    printf("Error is: %s\n", strerror(saved_error));
    

相关问题