【问题标题】:Using fscanf to read integers, strings, and real numbers使用 fscanf 读取整数、字符串和实数
【发布时间】:2018-04-25 22:18:43
【问题描述】:

使用 fscanf 读取整数、字符串和实数的组合时遇到问题。我承认我是 C 的新手程序员,但我不明白为什么我的代码不能正常工作。

sourcefile.txt的内容,fscanf使用的文件:

222 MSLET[Pa] 0-MSL 200507011200 200507021200 101226.063
223 MSLET[Pa] 0-MSL 200507011200 200507021200 9999.000
224 MSLET[Pa] 0-MSL 200507011200 200507021200 101217.063
222 PRMSL[Pa] 0-MSL 200507011200 200507021200 101226.063
223 PRMSL[Pa] 0-MSL 200507011200 200507021200 9999.000

我的c代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

int main (void)
{
    FILE *input;
    input = fopen("C:/sourcefile.txt", "r"); 
    char var[30], level[30];
    int loc, datecycle, datevalid;
    float value;
                                                             
    while (fscanf(input,"%d %[^ ] %[^ ] %d %d %f", &loc, var, level,         
&datecycle, &datevalid, &value) == 6) {
        fscanf(input,"%d %[^ ] %[^ ] %d %d %f", &loc, var, level, &datecycle,  
&datevalid, &value);
        printf("%d %s %s %d %d %f\n", loc, var, level, datecycle,
datevalid,value);
    }                                                                                                       
                                              
    fclose(input);
    return 0;               
}

C 代码的输出:

223 MSLET[Pa] 0-MSL -1356451712 -1356441712 9999.000
222 PRMSL[Pa] 0-MSL -1356451712 -1356441712 101226.063
223 PRMSL[Pa] 0-MSL -1356451712 -1356441712 9999.000

问题 #1

  1. 5 行中只读取了 3 行。我不明白为什么。

  2. datecycle 和 datevalid 的 printf 输出与 输入。我不明白为什么。

问题 #2

对于第 2 列中的字符串条目(例如 MSLET[Pa]),改为 使用 [^ ] 读取字符串(读取直到遇到空格),我 可能想要阅读,直到我遇到“]”(例如,MSLET[Pa] 中的“]”)。 我的理解是我会写[^]]。对吗?

如果能提供任何帮助,我们将不胜感激。

【问题讨论】:

  • 您应该解释“无法正常工作”(最好显示您获得的输出并解释它与您的预期有何不同)
  • 您读入datecycledatevalid 的值可能超过int 的最大大小。然后你丢弃每一行。
  • 为了澄清,我试图准确地输出输入,使用 fscanf 和 printf。通过将 while 语句更改为“while (!feof(input)) {”,我能够读取所有行。通过替换“浮点值;”具有“双重价值”;并将说明符“%f”替换为“%lf”,我能够读取并输出 datecycle 和 datevalid 的正确值(小数点左侧)。谢谢MM的帮助。
  • 请正确格式化帖子。您可以拥有多个代码块,并且您必须意识到在代码块中包含问题描述是没有意义的。尤其是不在与代码相同的块中。同时修复缩进。

标签: c scanf


【解决方案1】:

虽然是一个较老的问题,但值得回答。您没有获得datacycledatevalid 的有效数据的原因是您尝试使用转换为int 来读取的输入超出了可表示为int 的值。例如200507011200 超过有符号整数表示的最大值两个数量级。

但是,简单地使用更大的存储类型来容纳这些值将导致以后难以分离字符串的年、月、日、时间部分。更好的方法是将datacycledatevalid 读取为字符串。如果需要,您可以稍后转换为数值,或者更有可能将其转换为年、月、日、时间。

此外,直接使用fscanf() 读取输入文件是一种脆弱的方法。一行格式的单个错误将破坏从该点向前读取的所有数据。

相反,将每一行读入缓冲区(字符数组),然后使用sscanf() 解析成单独的值,从而将读取和转换解耦,从而允许完全读取每一行。在这种情况下,如果一行包含无效字符等...只有该行中的值解析会失败,并且可以正确读取所有剩余的行。

不要使用 MagicNumbers 或硬编码文件名。相反,要么提供文件名作为程序的第一个参数(这就是 main()int argc, char **argv 参数),要么提示用户并将文件名作为输入。你不应该仅仅为了从不同的文件名读取数据而重新编译你的代码。 30 在您的代码中是一个 MagicNumber。如果你需要一个常量,#define 一个或者使用一个全局的enum。例如:

#define MAXC 1024       /* if you need a constant, #define on (or more) */
#define MAXVL  32
#define NELEM  16

其中MAXC 是从每行读取的最大字符数(例如,保存该行的字符数组的大小),MAXVL 的大小为 varlevelNELEM数组中用于保存所有值的元素数量和16 的一般常量,也用于datacycledatevalid 存储的字符串大小。

要保存每行数据以便您的程序可以处理它,请创建一个结构来保存 locvarleveldatacycledatevalid 和 @987654349 的值@,并简单地声明一个结构数组。这样,当您读取和转换每一行时,您可以将值存储在一个数组中以供整个程序使用,例如

typedef struct {        /* store datacycle & datevalid as strings */
    char var[MAXVL], level[MAXVL], datacycle[NELEM], datevalid[NELEM];
    int loc;
    float value;
} mydata_type;

将要读取的文件名作为程序的第一个参数(如果没有给出值,则默认从stdin 读取),您可以执行以下操作:

int main (int argc, char **argv) {
    
    char buf[MAXC];                             /* buffer to hold each line */
    size_t n = 0;                               /* array element counter */
    mydata_type arr[NELEM] = {{ .var = "" }};   /* array of struct */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

您可以读取和存储每一行​​,直到您的数组已满或到达文件末尾,方法是将每一行循环并读取到buf,然后如上所述用sscanf() 分隔值,例如

    /* while array not full and line read */
    while (n < NELEM && fgets (buf, MAXC, fp)) {
        mydata_type tmp;    /* temporary structure to parse data into */
        /* parse data from buf into temporary struct and VALIDATE */
        if (sscanf (buf, "%d %s %s %s %s %f", &tmp.loc, tmp.var, tmp.level,
                    tmp.datacycle, tmp.datevalid, &tmp.value) == 6) {
            arr[n] = tmp;   /* on success, assign to array element */
            n += 1;         /* increment element counter */
        }
        else {
            fputs ("error: invalid line format.\n", stderr);
        }
    }

完成后关闭文件,然后使用程序中需要的数据。例如,您可以输出从每一行读取的每个值,同时使用sscanf()datacycledatevalid 转换为年、月、日和时间。一个例子是:

void prn_mydata (mydata_type *arr, size_t n)
{
    for (size_t i = 0; i < n; i++) {
        int mc, dc, yc, tc,     /* integer values for datacycle components */
            mv, dv, yv, tv;     /* integer values for datevalid components */
        
        /* parse string values for datacycle & datevalid into components */
        if (sscanf (arr[i].datacycle, "%4d%2d%2d%4d", &yc, &mc, &dc, &tc) != 4)
            return;
        if (sscanf (arr[i].datevalid, "%4d%2d%2d%4d", &yv, &mv, &dv, &tv) != 4)
            return;
        
        /* output results */
        printf ("\n%d\n%s\n%s\n%s  %d-%02d-%02d:%d\n"
                "%s  %d-%02d-%02d:%d\n%.3f\n", 
                arr[i].loc, arr[i].var, arr[i].level,
                arr[i].datacycle, yc, mc, dc, tc, 
                arr[i].datevalid, yv, mv, dv, tv, 
                arr[i].value);
    }
}

将其完全放入您将拥有的示例程序中:

#include <stdio.h>
#include <string.h>

#define MAXC 1024       /* if you need a constant, #define on (or more) */
#define MAXVL  32
#define NELEM  16

typedef struct {        /* store datacycle & datevalid as strings */
    char var[MAXVL], level[MAXVL], datacycle[NELEM], datevalid[NELEM];
    int loc;
    float value;
} mydata_type;

void prn_mydata (mydata_type *arr, size_t n)
{
    for (size_t i = 0; i < n; i++) {
        int mc, dc, yc, tc,     /* integer values for datacycle components */
            mv, dv, yv, tv;     /* integer values for datevalid components */
        
        /* parse string values for datacycle & datevalid into components */
        if (sscanf (arr[i].datacycle, "%4d%2d%2d%4d", &yc, &mc, &dc, &tc) != 4)
            return;
        if (sscanf (arr[i].datevalid, "%4d%2d%2d%4d", &yv, &mv, &dv, &tv) != 4)
            return;
        
        /* output results */
        printf ("\n%d\n%s\n%s\n%s  %d-%02d-%02d:%d\n"
                "%s  %d-%02d-%02d:%d\n%.3f\n", 
                arr[i].loc, arr[i].var, arr[i].level,
                arr[i].datacycle, yc, mc, dc, tc, 
                arr[i].datevalid, yv, mv, dv, tv, 
                arr[i].value);
    }
}

int main (int argc, char **argv) {
    
    char buf[MAXC];                             /* buffer to hold each line */
    size_t n = 0;                               /* array element counter */
    mydata_type arr[NELEM] = {{ .var = "" }};   /* array of struct */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
    
    /* while array not full and line read */
    while (n < NELEM && fgets (buf, MAXC, fp)) {
        mydata_type tmp;    /* temporary structure to parse data into */
        /* parse data from buf into temporary struct and VALIDATE */
        if (sscanf (buf, "%d %s %s %s %s %f", &tmp.loc, tmp.var, tmp.level,
                    tmp.datacycle, tmp.datevalid, &tmp.value) == 6) {
            arr[n] = tmp;   /* on success, assign to array element */
            n += 1;         /* increment element counter */
        }
        else {
            fputs ("error: invalid line format.\n", stderr);
        }
    }
    
    if (fp != stdin)        /* close file if not stdin */
        fclose (fp);
    
    prn_mydata (arr, n);    /* print the results */
}

使用/输出示例

使用dat/sourcefile.txt 中的示例数据,您将使用该程序并接收以下输出,其中该行的每个组件作为组的一部分打印在单独的行上:

$ ./bin/read_sourcefile dat/sourcefile.txt

222
MSLET[Pa]
0-MSL
200507011200  2005-07-01:1200
200507021200  2005-07-02:1200
101226.062

223
MSLET[Pa]
0-MSL
200507011200  2005-07-01:1200
200507021200  2005-07-02:1200
9999.000

224
MSLET[Pa]
0-MSL
200507011200  2005-07-01:1200
200507021200  2005-07-02:1200
101217.062

222
PRMSL[Pa]
0-MSL
200507011200  2005-07-01:1200
200507021200  2005-07-02:1200
101226.062

223
PRMSL[Pa]
0-MSL
200507011200  2005-07-01:1200
200507021200  2005-07-02:1200
9999.000

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

    猜你喜欢
    • 2017-05-03
    • 2012-07-17
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 2021-07-15
    • 2014-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多