strtol没有改变errno

时间:2016-02-07 22:57:21

标签: c errno strtol

我正在开发一个程序,该程序在给定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 个答案:

答案 0 :(得分:1)

当不能执行转换时,

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

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

答案 1 :(得分:1)

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

您的代码还有其他问题:

  • 使用BUFSIZ复制字符串不正确:如果源字符串长于localTimestrncpy将不会以空值终止。避免使用:,这是一种几乎不符合目的的难以理解的功能。
  • 在这种情况下,您无需清除'\0'strtollocalTime[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: 1

此方法仍然接受错误的字符串,例如12:00000002:1public class Test{ @Tested ClassA objA; @Test(expected = MyException.class){ String expectedVar = "expectedVar"; new Expectations(objA) { { objA.someMethod(); result = expectedVar; } }; // So here is error, when I debug the programm it doesn't even enter following method. // If I connent out new Expectations(){{...}} block, only then the programm // will enter the method objA.execute() objA.execute(); } 。手工解析字符串似乎是最简洁有效的解决方案。

答案 2 :(得分: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;
}

答案 3 :(得分:0)

hours = strtol(localTime, &par, base);语句之后,您必须先保存errno的值。因为在此声明之后,您将调用同样设置printf()的{​​{1}}语句。

errno

所以在这个语句中“errno”给出printf("errno is: %d\n", errno); 的错误指示而不是printf() ...这样做在调用任何库函数之前保存“errno”,因为大多数库函数都与之交互“错误号”。 正确使用是:

strtol()

现在检查一下。它肯定会提供正确的输出......还有一件事要将此hours = strtol(localTime, &par, base); int saved_error = errno; // Saving the error... printf("errno is: %d\n", saved_error); 转换为某个有意义的字符串以表示错误使用errno函数如下:

strerror()