【问题标题】:The correct way to use fscanf in C++在 C++ 中使用 fscanf 的正确方法
【发布时间】:2017-06-16 05:43:55
【问题描述】:

我正在尝试从以下格式的 csv 文件中读取数据:

a_x,a_y,v_x,o_rear,o_front,theta,type
-0.040,10.206,-0.000,0.000,-0.000,-0.002,plane
1.269,9.813,0.011,0.043,0.091,-0.002,plane
1.266,9.985,0.021,0.086,0.177,-0.002,plane
1.257,10.002,0.032,0.130,0.265,-0.002,plane
1.256,10.004,0.043,0.173,0.353,-0.002,plane
1.257,10.003,0.053,0.216,0.440,-0.002,plane
1.258,10.002,0.064,0.259,0.527,-0.002,plane
1.258,10.002,0.074,0.302,0.615,-0.002,plane

我写了以下代码:-

FILE *in_file = fopen(argv[1], "r");
        int i=0, s=0;

        char *temp;
        fscanf(in_file,"%s",temp);
        printf("%s\n",temp);
        while(i<100){
            float a_x,a_y,v_x,o_rear,o_front,theta;
            char *type;
            if((s = fscanf(in_file,"%f,%f,%f,%f,%f,%f,%[^,]",&a_x,&a_y,&v_x,&o_rear,&o_front,&theta,type))!=7)
                printf("%7d,%d,%f,%f,%f,%f,%f,%f,%s\n",++i,s,a_x,a_y,v_x,o_rear,o_front,theta,type);
            else{
                printf("ALL GOOD: %7d\n",++i);
            }
        }

我遇到了分段错误。在运行 gdb 时,我发现它发生在 fscanf(in_file,"%s",temp); 行并出现以下错误:-

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff71c6f64 in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=argptr@entry=0x7fffffffda48, errp=errp@entry=0x0)
    at vfscanf.c:1107
1107    vfscanf.c: No such file or directory.

堆栈跟踪:-

#0  0x00007ffff71c6f64 in _IO_vfscanf_internal (s=<optimized out>, format=<optimized out>, argptr=argptr@entry=0x7fffffffda48, 
    errp=errp@entry=0x0) at vfscanf.c:1107
#1  0x00007ffff71cce47 in ___vfscanf (s=<optimized out>, format=<optimized out>, argptr=argptr@entry=0x7fffffffda48) at vfscanf.c:3066
#2  0x00007ffff71d47b7 in __fscanf (stream=<optimized out>, format=<optimized out>) at fscanf.c:31
#3  0x0000555555555186 in main (argc=2, argv=0x7fffffffdc58) at data_handler.cpp:23

令人惊讶的是,当我使用type 变量转储第一行时,我没有遇到任何段错误。相反,fscanf 第一次返回 7,然后返回 0。因此我可以正确读取第二行,但不能正确读取其他行。我究竟做错了什么?我想了解为什么会发生这种行为。

【问题讨论】:

  • char *temp; fscanf(in_file,"%s",temp); -- 这是错误的。您正在读取一个未初始化的指针。
  • 你给temp赋值了吗?如果不是,为什么要将其(无意义的)值传递给fscanf
  • 但是当我在循环外声明 type 变量并使用它时会发生什么?
  • 我纠正了它,它仍然在发生 -- 你的程序有几个问题,因为你在其他地方使用未初始化的指针变量时犯了同样的错误,即其他fscanf 电话。反正你用的是C++,那你为什么要写C代码呢?
  • 如果您使用 C++ 流,则无需为您要读取的内容使用格式说明符。C++ 流知道您要读取的类型——无需告诉编译器%f%s

标签: c++ io segmentation-fault scanf


【解决方案1】:

在 C++ 中读取文件的惯用方式是使用 istream::operator&gt;&gt;,您可以找到该 here 的详细文档。

它工作得更好,并且更难出现分段错误和未定义的行为。

如果您打算以 C 方式解决此问题,则应使用 C 编译器(而不是 C++ 编译器),此代码应使用 .c 扩展名,此问题应使用[c] 标签。

不过,无论您是 C++ 程序员还是 C 程序员,都希望阅读手册。例如an fscanf manual,它告诉你如何正确使用fscanf,从而回答了这个问题。它说:

s 匹配不是空白字符的字节序列。应用程序应确保相应的参数是指向charsigned charunsigned char 的数组的初始字节的指针,其大小足以接受序列和终止空字符代码,应自动添加。

虽然手册页提供了重点,但我在这里强调了您需要理解的最重要的部分:temp 需要指向某物

就目前而言,您的变量 temp 可以指向 anythingnothing 例如,它可以包含一个 空指针值,表示它会指向nothing;你没有告诉它不这样做,是吗?

像这样改变temp的声明:

char temp[512];             // ... or
char *temp = new char[512]; // ... etc

其中任何一个都将确保fscanf(in_file, "%s", temp); 中的表达式temp 指向数组

【讨论】:

  • 确实我没有按照惯用的方式。但是我想做的事情会以惯用的方式变得不必要的冗长和不可读。如果文件是空格分隔而不是逗号分隔,那会更容易。
【解决方案2】:

您在对fscanf 的调用中使用了temp,但没有确保它指向某个可以读取数据的有效内存。这会导致未定义的行为。

你可以使用:

// Use any number that is big enough for your needs
char temp[200];

// Make sure you provide the maximum number of characters to
// read to temp, always leaving space for the null terminator.
fscanf(in_file, "%199s", temp);

【讨论】:

  • 那为什么这没有给出段错误:
  • 文件 *in_file = fopen(argv[1], "r"); int i=0, s=0;字符 * 类型; fscanf(in_file,"%s",type); printf("%s\n",type); while(i
  • @MeetTaraviya,根据定义,无法定义未定义的行为。任何事情都可能发生,包括看似理智的行为。你无法理解它。
  • @MeetTaraviya why does this does not give a seg fault 未定义意味着未定义。它可以给出错误,它可以看起来工作,它可以做任何奇怪的事情,它可以爆炸,......只是不要写UB代码。
【解决方案3】:

正如@PaulMcKenzie 指出的那样,我没有初始化我的指针。所以我到处使用char x[100] 而不是char *x。另一个问题是格式字符串错误:应该是"%f,%f,%f,%f,%f,%f,%s" 而不是"%f,%f,%f,%f,%f,%f,%[^,]"

【讨论】:

    猜你喜欢
    • 2016-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多