对于作为问题的一部分发布的文件,您已经有了一个很好的答案,但是,如果您的文本行在表示负符号值的数字之前包含 '-' 符号,first - last 的结果将不正确。所有 C 字符串到整数的转换都将在要转换的值之前接受前导 +/-,以指示正数或负数。如果您的输入可以包含负值,则需要通过包含要转换的数字来保留 '-' 符号。例如,如果您的输入文件是:
36@xL!?\-8
28?>\4
-42<pX%7
37@#-5
31kL%^?>\<#%5
将-8, -42 和-5 作为文件中的整数值,答案将完全不同。现在,如果根据您的具体分配不可能做到这一点,那么您可以跳过保留 '-',但对于将阅读世界文本转换为有符号值而言,这一点至关重要。
在字符串中查找有符号数字的开头进行转换的一种方法是简单地在字符串中向前扫描(类似于strpbrk() 会做的事情)寻找开头数字或'-'(以先发生者为准) .如果首先出现'-',则检查下一个字符是否为数字。你可以用一个简单的循环来做到这一点,例如
/** scan forward in 'p' to find beginning of next valid signed integer.
* returns pointer to beginning of signed int on success, NULL otherwise.
*/
const char *nextdigit (const char *p)
{
while (*p) {
/* if digit or +/- followed by a digit */
if (isdigit(*p) ||
((*p == '-' || *p == '+') && isdigit(*(p + 1))))
return p;
p++;
}
return NULL;
}
一旦你找到了一个数字的开头,你就需要使用一个对转换提供错误检查的函数。 atoi() 为转换提供零诊断,并将静默返回0 作为atoi("my cow"); 转换的有效数字您将无法指示数字是否实际转换或结果是否超过所有整数类型的存储大小。 atoi() 完全没有错误,即使提供了 200 位字符串作为输入。至少使用sscanf,它至少会提供yes/no、true/false 来判断是否发生了有效的转换,或者更好的是使用strtol,它提供有关转换的完整错误报告。
例如,您可以编写一个短函数来获取指向字符串的指针的地址,使用上面的nextdigit() 函数,然后使用strtol 来完全验证结果,将errno 设置回用于验证任何错误的调用者并返回整数转换的结果(或0错误)如下:
/** returns next integer in string pointed to by p, or sets errno and returns
* zero on error.
*/
int getnextint (char **p)
{
int nextint = 0;
errno = 0;
if ((*p = (char*)nextdigit(*p))) {
char *endptr;
long tmp = strtol (*p, &endptr, 0);
if (*p == endptr) { /* validate digits converted */
fputs ("error: no digits converted.\n", stderr);
errno = EINVAL;
}
else if (errno) /* validate conversion */
fputs ("error: over/underflow occurred.\n", stderr);
/* validate tmp is in range of integer */
else if (INT_MIN <= tmp && tmp <= INT_MAX)
nextint = tmp;
else {
fputs ("error: value exceeds range of int.\n", stderr);
errno = ERANGE;
}
*p = (char*)nextdigit(endptr);
}
else
errno = EINVAL; /* if no digits found, set EINVAL */
return nextint;
}
(注意: 传递指针的地址,以便可以在函数内将指针更新到字符串中要转换的下一个整数的开头(或者NULL,如果没有更多留下)
要完成该示例,您可以添加所需的标头并编写一个简短的 main() 以从作为第一个参数提供的文件名中读取(如果未提供参数,则默认从 stdin 读取),这将找到每行中的第一个和最后一个整数并减去first - last 输出结果:
#include <stdio.h>
#include <stdlib.h> /* for strtol */
#include <string.h> /* for strcspn */
#include <limits.h> /* for INT_MIN/INT_MAX */
#include <errno.h> /* for errno */
#include <ctype.h> /* for isdigit */
#define ARSZ 100
#define MAXC 1024
... /* insert functions here */
int main (int argc, char **argv) {
char buf[MAXC] = "";
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line of input */
int arr[ARSZ] = {0};
char *p = buf;
size_t n = 0;
buf[strcspn(buf, "\r\n")] = 0;
while (n < ARSZ && p) {
arr[n] = getnextint (&p);
if (!errno)
n++;
}
if (n > 1)
printf ("%-19s : % 2d - % 2d = % 3d\n",
buf, *arr, arr[n-1], *arr - arr[n-1]);
else
fprintf (stderr, "%zu integer(s) in: '%s'\n", n, buf);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
输入文件示例
您的原始输入文件:
$ cat dat/last-first.txt
36@xL!?\8
28?>\4
42<pX%7
37@#5
31kL%^?>\<#%5
另一个带有负值和额外的无关行:
$ cat dat/last-first2.txt
36@xL!?\-8
28?>\4
-42<pX%7
Nothing to see!
37@#-5
31kL%^?>\<#%5
使用/输出示例
$ ./bin/fgets_strtol_any_last-first dat/last-first.txt
36@xL!?\8 : 36 - 8 = 28
28?>\4 : 28 - 4 = 24
42<pX%7 : 42 - 7 = 35
37@#5 : 37 - 5 = 32
31kL%^?>\<#%5 : 31 - 5 = 26
在带有负值和无关行的文件上运行时:
$ ./bin/fgets_strtol_any_last-first dat/last-first2.txt
36@xL!?\-8 : 36 - -8 = 44
28?>\4 : 28 - 4 = 24
-42<pX%7 : -42 - 7 = -49
0 integer(s) in: 'Nothing to see!'
37@#-5 : 37 - -5 = 42
31kL%^?>\<#%5 : 31 - 5 = 26
从不同文件之间的减法结果可以看出,在转换有符号值时是否保留前导 '-' 会有很大的不同。未来需要考虑的事情。
检查一下,如果您还有其他问题,请告诉我。