我正在开发一个程序,该程序在给定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
,所以我不能做错误处理。我错了什么?
答案 0 :(得分:1)
strtol
不需要生成错误代码。相反,你应该使用第二个参数来存储转换后的最终位置,并将其与初始位置进行比较。
顺便说一句,您的代码中还有许多其他错误,这些错误不会影响您看到的问题,但也应该修复,例如错误地使用strncpy
。
答案 1 :(得分:1)
正如其他人所解释的那样,如果strtol
无法执行任何转换,errno
可能不会更新errnor
。如果转换后的值不适合ERANGE
整数,则C标准仅将long
设置为strncpy
的文档。
您的代码还有其他问题:
BUFSIZ
复制字符串不正确:如果源字符串长于localTime
,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: 1
此方法仍然接受错误的字符串,例如12:00000002:1
或public 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()