[英]Trying to read a list of floats, integers, and strings from a file with sscanf() in C is not working as expected
我正在开发一个程序,可以从文件中读取非常大的表,确切地说是周期表。
struct periodic *createTable(){
char format[] ="%d\t%3s\t \
%20s\t%f\t \
%100[^\t]\t%f\t \
%d\t%f\t%d\t \
%d\t%d\t%20[^\t]\t \
%7s\t%17[^\t]\t \
%d\t%d\t%f\t \
%40[^\t]\t%7s\n";
struct periodic *tablePtr = malloc(sizeof(*tablePtr)*num_elements);
FILE *fp;
fp = fopen("periodictable.csv","r");
char buff[150];
int i,err;
for(i=0;i<num_elements;i++){
if(fgets(buff,150,fp)){
printf("%s\n",buff);
err = sscanf(buff,format,&(tablePtr->num),&(tablePtr->sym),&(tablePtr->name),
&(tablePtr->weight),&(tablePtr->config),&(tablePtr->neg),
&(tablePtr->neg),&(tablePtr->rad),&(tablePtr->ion_rad),
&(tablePtr->vdW_rad),&(tablePtr->IE_1),&(tablePtr->EA),
&(tablePtr->oxi_st),&(tablePtr->stn_st),&(tablePtr->bond_type),
&(tablePtr->melt),&(tablePtr->boil),&(tablePtr->dens),
&(tablePtr->type),&(tablePtr->year));
printf("\n\nMatches:%d\n",err);
printf("%d\n",tablePtr->num);
printf("%s\n",tablePtr->sym);
printf("%s\n",tablePtr->name);
printf("%f\n",tablePtr->weight);
printf("%s\n",tablePtr->config);
printf("%f\n",tablePtr->neg);
printf("%d\n",tablePtr->rad);
printf("%f\n",tablePtr->ion_rad);
printf("%d\n",tablePtr->vdW_rad);
printf("%d\n",tablePtr->IE_1);
printf("%d\n",tablePtr->EA);
printf("%s\n",tablePtr->oxi_st);
printf("%s\n",tablePtr->stn_st);
printf("%s\n",tablePtr->bond_type);
printf("%d\n",tablePtr->melt);
printf("%d\n",tablePtr->boil);
printf("%f\n",tablePtr->dens);
printf("%s\n",tablePtr->type);
printf("%d\n",tablePtr->year);
}
}
}
format []是包含我所有格式代码的字符串。将读取文件,并将每一行放入名为buff的字符串中。 读取Buff并解析每行的值。
从第一行开始,我遇到了一些问题。
前五个值在下面的那些打印语句中正确返回。
1 H氢1.00794 1s1
但是接下来的三个值只是零,从那里开始,所有一切都从那里消失了。 我不知道出了什么问题以及如何解决。 任何帮助将非常感激!
正如评论中指出的那样,已经有25年以上的历史了,因为有必要用反斜杠换行符(以及下一行的空格太多)来编写多行字符串。 您可以使用字符串串联:
"this string" " and this string"
将被视为:
"this string and this string"
即使两个字符串位于单独的行中,但在行尾也没有反斜杠。 (我稍微夸大了一点。这意味着没有必要使用的标准已有25年的历史了-C89 / C90。但是该工具相对广泛的使用可能需要5年的时间。不过,当前千年中编写的任何代码都可以使用字符串串联,而不是在行尾使用反斜杠。)
您将两个值读入&tablePtr->neg
是因为您两次传递了该值(但是您尝试将其读为float
和int
-这是“让您放心”的时间)。 请注意,格式中的\\t
与空格相同-格式字符串中的任何空格字符(不在扫描集中)都与零个或多个空格字符匹配。 同样,由于配置最多可以包含100个字符,因此该行的150个字符非常短(因此该成员必须至少为char config[101];
)。 具有结构定义会有所帮助; 这是必须进行反向工程的麻烦。
我还建议使用更多的编译器警告标志,或者使用更好的编译器。 但是,这并不完全有帮助。 由于格式字符串位于变量而非常量中,因此多余的警告不可用。 我通过创建一个宏来为格式创建一个恒定的字符串来解决了这个问题,然后一切都变得&tablePtr->sym
因为您传递了&tablePtr->sym
(这是一个char (*)[4]
而不是char *
)— drop &
。
您将年份读为7个字符的字符串,但尝试将其打印为整数,这不是一个好主意。
这将导致此制造的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct periodic
{
int num;
char sym[4];
char name[21];
float weight;
char config[101];
float neg;
int rad;
float ion_rad;
int vdW_rad;
int IE_1;
int EA;
char oxi_st[21];
char stn_st[8];
char bond_type[18];
int melt;
int boil;
float dens;
char type[41];
char year[8];
};
#define FORMAT "%d\t%3s\t" \
"%20s\t%f\t" \
"%100[^\t]\t%f\t" \
"%d\t%f\t%d\t" \
"%d\t%d\t%20[^\t]\t" \
"%7s\t%17[^\t]\t" \
"%d\t%d\t%f\t" \
"%40[^\t]\t%7s\n"
int num_elements = 120;
struct periodic *createTable(void);
struct periodic *createTable(void)
{
// char format[] = "%d\t%3s\t" // num, sym
// "%20s\t%f\t" // name, weight
// "%100[^\t]\t%f\t" // config, neg
// "%d\t%f\t%d\t" // rad, ion_rad, vdW_rad
// "%d\t%d\t%20[^\t]\t" // IE_q, EA, oxi_st
// "%7s\t%17[^\t]\t" // stn_st, bond_type
// "%d\t%d\t%f\t" // melt, boil, dens
// "%40[^\t]\t%7s\n"; // type, year
struct periodic *tablePtr = malloc(sizeof(*tablePtr) * num_elements);
FILE *fp;
fp = fopen("periodictable.csv", "r");
char buff[150];
int i, err;
for (i = 0; i < num_elements; i++)
{
if (fgets(buff, 150, fp))
{
printf("Data: %s\n", buff);
err = sscanf(buff, FORMAT, &(tablePtr->num), (tablePtr->sym), (tablePtr->name),
&(tablePtr->weight), (tablePtr->config), &(tablePtr->neg),
/*&(tablePtr->neg),*/ &(tablePtr->rad), &(tablePtr->ion_rad),
&(tablePtr->vdW_rad), &(tablePtr->IE_1), &(tablePtr->EA),
(tablePtr->oxi_st), (tablePtr->stn_st), (tablePtr->bond_type),
&(tablePtr->melt), &(tablePtr->boil), &(tablePtr->dens),
(tablePtr->type), (tablePtr->year));
if (err != 19)
{
fprintf(stderr, "Conversion failure: %d OK\n", err);
break;
}
printf("\n\nMatches:%d\n", err);
printf("Num: %d\n", tablePtr->num);
printf("Sym: %s\n", tablePtr->sym);
printf("Name: %s\n", tablePtr->name);
printf("Weight: %f\n", tablePtr->weight);
printf("Config: %s\n", tablePtr->config);
printf("Neg: %f\n", tablePtr->neg);
printf("Rad: %d\n", tablePtr->rad);
printf("IonRad: %f\n", tablePtr->ion_rad);
printf("vdWRad: %d\n", tablePtr->vdW_rad);
printf("IE_1: %d\n", tablePtr->IE_1);
printf("EA: %d\n", tablePtr->EA);
printf("Oxi_St: %s\n", tablePtr->oxi_st);
printf("Stn_St: %s\n", tablePtr->stn_st);
printf("BondTp: %s\n", tablePtr->bond_type);
printf("Melt: %d\n", tablePtr->melt);
printf("Boil: %d\n", tablePtr->boil);
printf("Dense: %f\n", tablePtr->dens);
printf("Type: %s\n", tablePtr->type);
printf("Year: %s\n", tablePtr->year);
}
}
return tablePtr;
}
int main(void)
{
struct periodic *tbl = createTable();
free(tbl);
return 0;
}
请注意, sscanf()
参数周围的括号是多余的。 一行,例如:
&(tablePtr->weight), (tablePtr->config),
完全可以写成没有括号的形式:
&tablePtr->weight, tablePtr->config,
给定一个单行数据文件,其中包含大部分虚构的氢数据:
1 H Hydrogen 1.00794 1s1 -1.0001999 2 3.0002999 4 5 6 Oxy Stn Bond -234 -236 1.01E-5 Gas 1723
程序产生了输出:
Data: 1 H Hydrogen 1.00794 1s1 -1.0001999 2 3.0002999 4 5 6 Oxy Stn Bond -234 -236 1.01E-5 Gas 1723
Matches:19
Num: 1
Sym: H
Name: Hydrogen
Weight: 1.007940
Config: 1s1
Neg: -1.000200
Rad: 2
IonRad: 3.000300
vdWRad: 4
IE_1: 5
EA: 6
Oxi_St: Oxy
Stn_St: Stn
BondTp: Bond
Melt: -234
Boil: -236
Dense: 0.000010
Type: Gas
Year: 1723
在一个输入中有19列很痛苦。 将字符串文字(伪装为宏)用于格式字符串会有所帮助; 至少GCC可以错误检查类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.