【问题标题】:fscanf / fscanf_s overwriting arraysfscanf / fscanf_s 覆盖数组
【发布时间】:2015-06-12 19:00:39
【问题描述】:

我正在尝试使用 fscanf 为结构数组加载一些默认值,类似于

#define NUMPIECES 21
typedef struct{
    short map[5][5];
    short rotation;
    short reflection;
} mytype_t;
typedef struct{
    mytype_t p[NUMPIECES];
} mytypelist_t;

数据存储在文本文件中,如下所示(以不同的值重复多次):

0 0 0 0 0
0 0 0 0 0 
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
1 2
[...]

我正在使用 fscanf / fscanf_s 读取值(两者都尝试过),如下所示:

mytypelist_t list;
FILE * f;
[...]
for (i=0; i<NUMPIECES; i++){
    for (j=0; j<5; j++){
        fscanf(f,"%d%d%d%d%d", &(list->p[i].map[j][0]),
                                &(list->p[i].map[j][1]),
                                &(list->p[i].map[j][2]),
                                &(list->p[i].map[j][3]),
                                &(list->p[i].map[j][4]));
    }
    fscanf(f,"%d %d", &(list->p[i].rotations), &(list->p[i].reflection));
}

但是,VS2012 最后抛出了一个异常,说列表已损坏。调试显示,在阅读了上面示例文本的前四行之后,struct的“map”部分包含以下内容

map = [ 0 0 0 0 0 ]
      [ 0 0 0 0 0 ]
      [ 0 0 1 0 0 ]
      [ 0 0 0 0 0 ]
      [ 0 X X X X ]

其中 X 是未初始化的值。

似乎 fscanf 正在尝试“空终止”我的整数数组或类似的数组,并且正在覆盖后续行的第一个元素。我之所以发现它,是因为 VS 在退出时抛出了异常——否则数据会被完美读取(额外的 0 会被下一次 fscanf 调用覆盖)。

这是 fscanf 的副产品吗?还是我忽略了一个错误?

(在 VS2012 上编译/测试)

【问题讨论】:

  • 您已将 mytype_t.map 声明为 5x5,但您的示例文本文件为 4x5。那是错字吗?如果不是,那是你的问题。 fscanf() 正在尝试从一行中读取 5 个整数,但只有 2 个可用。

标签: c scanf


【解决方案1】:

我认为 fscanf 在你给它指向短裤的时候试图填充整数。 fscanf 不知道它正在填充的字段的实际类型 int;它依赖于格式说明符。我不知道任何“短”格式说明符。所以我要么将你的数据字段更改为整数,要么扫描成整数然后复制到你的数据结构中的短裤

【讨论】:

  • %hd 是将short * 参数与scanf 系列函数相匹配的格式说明符。
【解决方案2】:

使用short 格式说明符"%hd" 并检查fscanf() 的结果

int cnt = fscanf(f,"%hd%hd%hd%hd%hd", &(list->p[i].map[j][0]),
    &(list->p[i].map[j][1]), &(list->p[i].map[j][2]),
    &(list->p[i].map[j][3]), &(list->p[i].map[j][4]));
if (cnt != 5) Handle_MissingData();

由于数据每行有5个数字,建议阅读,然后扫描。

char buf[5*22];
if (fgets(buf, sizeof buf, f) == NULL) Handle_EOF();
int cnt = sscanf(buf,"%hd%hd%hd%hd%hd", &(list->p[i].map[j][0]),
    &(list->p[i].map[j][1]), &(list->p[i].map[j][2]),
    &(list->p[i].map[j][3]), &(list->p[i].map[j][4]));
if (cnt != 5) Handle_MissingData();

如果在没有short 格式说明符的系统上...

char buf[5*22];
if (fgets(buf, sizeof buf, f) == NULL) Handle_EOF();
int tmp[5];
int cnt = sscanf(buf,"%d%d%d%d%d", 
    &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4]);
for (i=0; i<cnt; i++)) {
  list->p[i].map[j][i] = tmp[i];
}
if (cnt != 5) Handle_MissingData();

【讨论】:

  • 使用 fgets+sscanf 而不是 fscanf 对受控和已知输入有什么好处?
  • @wilcroft 1) 对于具有挑战性的输入,尤其是包含空格但不包含行尾的字符串,fgets/sscanf/strtok/strod 等更容易编码。 2) 输入只是“已知”之后,而不是之前读取。 fgets()fscanf() 相比,合并错误处理更简单。
  • “已知”输入,在这种情况下,指的是我预定义的输入,因此保证匹配。我为解析半通用用户输入的代码添加了额外的预防措施。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-09
  • 2018-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-04
相关资源
最近更新 更多