我试图从文件中读取14位长的十六进制数字,然后打印它们。我的想法是使用long long int并使用fscanf读取文件中的行,就像它们是字符串一样,然后使用atoll将字符串转换为十六进制数字。问题是我根据valgrind得到了fscanf线上的seg值,我完全不知道为什么。这是代码:
#include<stdio.h>
int main(int argc, char **argv){
if(argc != 2){
printf("error argc!= 2\n");
return 0;
}
char *fileName = argv[1];
FILE *fp = fopen( fileName, "r");
if(fp == NULL){
return 0;
}
long long int num;
char *line;
while( fscanf(fp, "%s", line) == 1 ){
num = atoll(line);
printf("%x\n", num);
}
return 0;
}
答案 0 :(得分:4)
您确定要将数字作为字符串读取吗?为什么不允许scanf
为你做工作?
long long int num;
while( fscanf(fp, "%llx", &num) == 1 ){ // read a long long int in hex
printf("%llx\n", num); // print a long long int in hex
}
BTW,请注意ll
中%x
转换为printf
的{{1}}尺寸说明符 - 它定义的整数值为long long
类型。
修改
下面是两个循环的简单示例,它读取3行输入(连续行中有两个,没有和三个数字),带有'hex int'格式和'string'格式:
http://ideone.com/ntzKEi
对rewind
的调用允许第二个循环读取相同的输入数据。
答案 1 :(得分:1)
line
变量未初始化,因此当fscanf()
取消引用时,您会得到未定义的行为。
您应该使用:
char line[1024];
while(fgets(line, sizeof line, fp) != NULL)
进行加载。
如果您使用的是C99,则可能需要使用uint64_t
来保存该号码,因为这样可以清楚地表明14位十六进制数字(4 * 14 = 56)是否合适。
答案 2 :(得分:0)
其他答案都很好,但我想澄清你所看到的崩溃的实际原因。问题是:
fscanf(fp, "%s", line)
...本质上意味着“从文件中读取一个字符串,并将其存储在line
所指向的缓冲区中”。在这种情况下,您的line
变量尚未初始化,因此它不会指向任何位置。从技术上讲,这是未定义的行为;在实践中,结果通常是你在进程的地址空间中的某个任意位置写入;此外,由于它经常指向非法地址,因此操作系统可以检测并将其报告为违规或类似行为,如您所见。
请注意,带有fscanf
转换的%s
不一定会读取整行 - 它会读取由空格分隔的字符串。如果它们为空,它可能会跳过行,并且它可能会从一行读取多个字符串。如果您知道输入文件的精确格式(例如,每行总是有一个值),这可能无关紧要。
虽然在这种情况下可能只是使用适当的修饰符来读取十六进制数字(fscanf(fp, "%llx", &num)
),而不是读取字符串并尝试进行转换,但是在某些情况下你会这样做需要阅读字符串,特别是整行。根据您所使用的平台,该问题有各种解决方案。如果它是GNU系统(通常包括Linux)并且您不关心可移植性,则可以使用m
修饰符,并将line
更改为&line
:
fscanf(fp, "%ms", &line);
这会将指向line
的指针传递给fscanf
,而不是其值(未初始化),而m
会导致fscanf
分配缓冲区并存储其line
中的地址。然后,当您完成缓冲时,您应该free
缓冲区。有关详细信息,请查看Glibc手册。这种方法的好处是你不需要事先知道线长。
如果您没有使用GNU系统或者您关心可移植性,请使用fgets
而不是fscanf
- 这更直接,并允许您限制读取行的长度,这意味着你不会溢出一个固定的缓冲区 - 只要知道它会一次读取整行,不像fscanf
,如上所述。您应该将line
声明为char
- 数组而不是char *
,并为其选择合适的大小。 (请注意,您还可以为fscanf
指定“最大字段宽度”,例如fscanf(fp, "%1000s", line)
,但您确实可以使用fgets
)。