c - 尝试使用 C 中的 sscanf() 从文件中读取 float 、整数和字符串列表无法按预期工作

标签 c csv io formatting

我正在开发一个程序,该程序从文件中读取一个非常大的表,准确地说是周期表。

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

但是接下来的三个值只是零,从那里开始一切都崩溃了。我不知道出了什么问题以及如何解决。任何帮助将不胜感激!

最佳答案

诊断

正如评论中所指出的,自从有必要编写带有反斜杠换行符的多行字符串(并且下一行上有太多空格)以来,已经有四分之一个世纪了。您可以使用字符串连接:

"this string" " and this string"

将被视为相同:

"this string and this string"

即使两个字符串位于不同的行上且行尾没有反斜杠。 (我有点夸张了。意味着没有必要的标准已经有超过四分之一个世纪的历史了——C89/C90。但是,该设施相对广泛地可用可能需要五年的时间。尽管如此,在当前千年编写的任何代码都可以使用字符串连接而不是在行尾使用反斜杠。)

您将两个值读取到 &tablePtr->neg 中,因为您传递了两次该值(但您尝试将其读取为 floatint code> — 现在是“下定决心”的时间)。请注意,格式中的 \t 与空格相同 — 格式字符串中的任何空白字符(不在扫描集中)都匹配零个或多个空白字符。此外,考虑到配置最多可以有 100 个字符,该行的 150 个字符相当短(因此成员必须至少为 char config[101];)。有结构定义会很有帮助;必须对其进行逆向工程很麻烦。

我还建议使用更多的编译器警告标志,或者更好的编译器。然而,这并不完全有帮助。由于格式字符串位于变量中,而不是常量中,因此额外的警告不可用。我通过创建一个宏来为格式创建一个常量字符串来解决这个问题,然后一切都崩溃了,因为你传递了 &tablePtr->sym (这是一个 char (*)[ 4] 而不是 char *) — 删除 &

您将年份读取为 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 可以对类型进行错误检查。

关于c - 尝试使用 C 中的 sscanf() 从文件中读取 float 、整数和字符串列表无法按预期工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37759204/

相关文章:

c - 在纯 C 中,如何制作相当于 "map"的内容?

c - 客户端如何知道服务器是否已退出?

python - 按照 init 中显示的顺序表示一个类,无需硬编码

java - 从随机访问文件中读取数据

c - sprintf() 与 argv 的行为

java - 解释 Scanner close( ) 方法 Java

c - 如何创建一个为每个客户端创建一个新线程的服务器?

python - Pandas read_csv,读取缺少标题元素的 csv 文件

c# - 验证 CSV 文件

java - 关于else if实现的问题