[英]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.