c 使用 fgets 读取文件,strtok 导致分段错误
c reading a file using fgets, strtok causes segmentation fault
尝试逐行读取文件,
文件中的一行如下所示:
InputVector:0(1,3,4,2,40)
代码:
FILE *file = fopen(filename, "r");
char buff[26];
char *token;
while(fgets(buff, 26, (FILE*)file) != NULL) {
buff[strlen(buff)] = '[=11=]';
printf("%s\n", buff);
token = strtok(buff, INV_DELIM1);
printf("%s\n", token);
token = strtok(NULL, INV_DELIM2);
printf("%s\n", token);
while(token != NULL) {
token = strtok(NULL, INV_DELIM3);
printf("%s\n", token);
}
}
我的猜测是在 while 循环中 strtok()
没有在最后一个数字之后 return NULL
继续前进并导致分段错误。我尝试在 fgets()
之后将 "[=16=]"
添加到 buff
的末尾,但它没有做任何事情。
delim1: ":",
delim2: "(",
delim3: ",)"
我得到的输出是
InputVector:0(1,3,4,2,40)
InputVector
0
1
3
4
2
40
segfault
您重复出现如下代码:
token = strtok(NULL, INV_DELIM2);
printf("%s\n", token);
如果 strtok()
returns 为 NULL,则它会传递给 printf()
,由于 %s
,它期望获得一个指向有效的以 0 结尾的字符串的指针在格式参数中。 NULL 不是指向有效的以 0 结尾的字符串的指针,因此发生了不好的事情,在您的情况下表现为崩溃。
解决方法:在尝试使用之前确保strtok()
返回的指针不为NULL。
对未来的建议:学习如何使用 调试器 单步执行代码,并习惯使用 valgrind 来帮助跟踪内存问题。当您可以使用工具找出问题所在并准确了解问题所在时,您不必对正在发生的事情做出错误的猜测。
不要吝啬缓冲区大小。如果你最长的行可以是 25
个字符,那么不要使用 26
个字符作为缓冲区大小,而是:
#define MAXC 1024 /* constant for max characters in buf */
...
char buff[MAXC] = 1024;
(由您决定,128
与任何其他值一样工作,保证输入长度的任何变化都不会超出数组的范围。我宁愿缓冲区是1000 个字符太长而不是 1 个字符太短。)
然后通过检查长度来验证每个 fgets
调用,并且 buff
中的最后一个字符是 '\n'
字符,例如
while(fgets(buff, MAXC, file) != NULL) {
size_t len = strlen (buff);
if (len == MAXC - 1 && buff[len - 1] != '\n') {
fputs ("error: line too long.\n", stderr);
/* handle error - generally by reading and dicarding
* characters until '\n' or EOF encounterd and
* then either calling continue or break
*/
}
这将确保您在调用 strtok
之前拥有有效的字符串。
您不需要多个分隔符
然后,如评论中所述,不需要单独的分隔符。用 #define DELIM ":(,)\n"
定义或用 const char *delim = ":(,)\n"
声明的单个 delim
就足够了。然后你可以简单地循环遍历所有标记:
for (token = strtok(buff, delim); token; token = strtok(NULL, delim))
printf ("%s\n", token);
简短示例
#include <stdio.h>
#include <string.h>
#define MAXC 1024
int main (int argc, char **argv) {
char buff[MAXC] = "";
char *token = NULL;
const char *delim = ":(,)\n";
FILE *file = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!file) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while(fgets(buff, MAXC, file) != NULL) {
size_t len = strlen (buff);
if (len == MAXC - 1 && buff[len - 1] != '\n') {
fputs ("error: line too long.\n", stderr);
/* handle error - generally by reading and dicarding
* characters until '\n' or EOF encounterd and
* then either calling continue or break
*/
}
for (token = strtok(buff, delim); token; token = strtok(NULL, delim))
printf ("%s\n", token);
}
if (file != stdin) fclose (file); /* close file if not stdin */
return 0;
}
(如果你需要不同的结果,你可以调整delim
)
例子Use/Output
$ echo "InputVector:0(1,3,4,2,40)" | ./bin/strtok_delims
InputVector
0
1
3
4
2
40
检查一下,如果您还有其他问题,请告诉我。
尝试逐行读取文件, 文件中的一行如下所示:
InputVector:0(1,3,4,2,40)
代码:
FILE *file = fopen(filename, "r");
char buff[26];
char *token;
while(fgets(buff, 26, (FILE*)file) != NULL) {
buff[strlen(buff)] = '[=11=]';
printf("%s\n", buff);
token = strtok(buff, INV_DELIM1);
printf("%s\n", token);
token = strtok(NULL, INV_DELIM2);
printf("%s\n", token);
while(token != NULL) {
token = strtok(NULL, INV_DELIM3);
printf("%s\n", token);
}
}
我的猜测是在 while 循环中 strtok()
没有在最后一个数字之后 return NULL
继续前进并导致分段错误。我尝试在 fgets()
之后将 "[=16=]"
添加到 buff
的末尾,但它没有做任何事情。
delim1: ":",
delim2: "(",
delim3: ",)"
我得到的输出是
InputVector:0(1,3,4,2,40)
InputVector
0
1
3
4
2
40
segfault
您重复出现如下代码:
token = strtok(NULL, INV_DELIM2);
printf("%s\n", token);
如果 strtok()
returns 为 NULL,则它会传递给 printf()
,由于 %s
,它期望获得一个指向有效的以 0 结尾的字符串的指针在格式参数中。 NULL 不是指向有效的以 0 结尾的字符串的指针,因此发生了不好的事情,在您的情况下表现为崩溃。
解决方法:在尝试使用之前确保strtok()
返回的指针不为NULL。
对未来的建议:学习如何使用 调试器 单步执行代码,并习惯使用 valgrind 来帮助跟踪内存问题。当您可以使用工具找出问题所在并准确了解问题所在时,您不必对正在发生的事情做出错误的猜测。
不要吝啬缓冲区大小。如果你最长的行可以是 25
个字符,那么不要使用 26
个字符作为缓冲区大小,而是:
#define MAXC 1024 /* constant for max characters in buf */
...
char buff[MAXC] = 1024;
(由您决定,128
与任何其他值一样工作,保证输入长度的任何变化都不会超出数组的范围。我宁愿缓冲区是1000 个字符太长而不是 1 个字符太短。)
然后通过检查长度来验证每个 fgets
调用,并且 buff
中的最后一个字符是 '\n'
字符,例如
while(fgets(buff, MAXC, file) != NULL) {
size_t len = strlen (buff);
if (len == MAXC - 1 && buff[len - 1] != '\n') {
fputs ("error: line too long.\n", stderr);
/* handle error - generally by reading and dicarding
* characters until '\n' or EOF encounterd and
* then either calling continue or break
*/
}
这将确保您在调用 strtok
之前拥有有效的字符串。
您不需要多个分隔符
然后,如评论中所述,不需要单独的分隔符。用 #define DELIM ":(,)\n"
定义或用 const char *delim = ":(,)\n"
声明的单个 delim
就足够了。然后你可以简单地循环遍历所有标记:
for (token = strtok(buff, delim); token; token = strtok(NULL, delim))
printf ("%s\n", token);
简短示例
#include <stdio.h>
#include <string.h>
#define MAXC 1024
int main (int argc, char **argv) {
char buff[MAXC] = "";
char *token = NULL;
const char *delim = ":(,)\n";
FILE *file = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!file) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while(fgets(buff, MAXC, file) != NULL) {
size_t len = strlen (buff);
if (len == MAXC - 1 && buff[len - 1] != '\n') {
fputs ("error: line too long.\n", stderr);
/* handle error - generally by reading and dicarding
* characters until '\n' or EOF encounterd and
* then either calling continue or break
*/
}
for (token = strtok(buff, delim); token; token = strtok(NULL, delim))
printf ("%s\n", token);
}
if (file != stdin) fclose (file); /* close file if not stdin */
return 0;
}
(如果你需要不同的结果,你可以调整delim
)
例子Use/Output
$ echo "InputVector:0(1,3,4,2,40)" | ./bin/strtok_delims
InputVector
0
1
3
4
2
40
检查一下,如果您还有其他问题,请告诉我。