c语言 - 使用 fscanf 读取整数、字符串和实数



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

源文件的内容.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 %fn", 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. 来自日期周期和日期的有效 printf 输出与 输入。我不明白为什么。

问题 #2

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

任何可以提供的帮助将不胜感激。

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

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

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

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

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

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

其中MAXC是从每行读取的最大字符数(例如,用于保存该行的字符数组的大小),MAXVL表示varlevel的大小,NELEM数组中用于保存所有值的元素数,以及用于datacycledatevalid存储的字符串大小的一般常量16

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

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%dn%sn%sn%s  %d-%02d-%02d:%dn"
"%s  %d-%02d-%02d:%dn%.3fn", 
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%dn%sn%sn%s  %d-%02d-%02d:%dn"
"%s  %d-%02d-%02d:%dn%.3fn", 
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

仔细查看,如果您有其他问题,请告诉我。

相关内容

最新更新