2

我正在开发一个程序,该程序在给定一个 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()显然不会检测到错误,因为它没有更新errno1,所以我无法进行错误处理。我怎么了?

4

4 回答 4

3

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

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

于 2016-02-07T23:08:52.997 回答
1

正如其他人所解释的那样,如果它无法执行任何转换,strtol则可能不会更新。errnoC 标准仅记录在转换后的值不适合整数的情况下errnor设置为的文档。ERANGElong

您的代码还有其他问题:

  • 复制字符串strncpy不正确:如果源字符串长于BUFSIZlocalTime则不会以空值结尾。避免strncpy,一个几乎不符合目的的知之甚少的功能。
  • 在这种情况下,您无需清除:to '\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: 1or 12:00000002:1。手动解析字符串似乎是最简洁有效的解决方案。

于 2016-02-07T23:22:41.747 回答
0

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

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

因此,在此语句中,“errno”给出了错误指示,表示printf()not for 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)); 
于 2016-03-29T19:07:49.143 回答
0

一个有用的技巧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;
}
于 2016-02-08T04:04:42.093 回答