使用fscanf读取整数,字符串和实数

时间:2018-04-25 22:18:43

标签: scanf

使用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


My c-code is as follows:

#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;               
}

Output from c-code:

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

Issue #1

1. Only 3 of the 5 lines were read. I don't understand why.

2. The printf output from datecycle and datevalid are not the same as the
input. I don't understand why.   

Issue #2

With respect to the string entries in column 2 (e.g. MSLET[Pa]), instead
of using [^ ] to read in the string (read until I encounter a space), I
may want to read until I encounter the "]" (e.g. the "]" in MSLET[Pa]).
My understand is that I would write [^]]. Is that correct?  

Any help that can be provided would be greatly appreciated.

1 个答案:

答案 0 :(得分:0)

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

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

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

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

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

enum

其中 #define MAXC 1024 /* if you need a constant, #define on (or more) */ #define MAXVL 32 #define NELEM 16 是从每行读取的最大字符数(例如容纳该行的字符数组的大小),MAXCMAXVL 的大小,{ {1}} 和 var 是数组中用于保存所有值的元素数和一个通用常量 level 也用于 NELEM16 的字符串大小存储。

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

datacycle

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

value

您可以读取和存储每一行​​,直到您的数组已满,或者通过循环读取每一行并将其读入 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;
    }

完成后关闭文件,然后使用程序中所需的数据。例如,您可以输出从每行读取的每个值,同时使用 bufsscanf() /* 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); } } 转换为年、月、日和时间。一个例子是:

datacycle

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

datevalid

示例使用/输出

使用 sscanf() 中的示例数据,您将使用该程序并接收以下输出,其中该行的每个组成部分作为组的一部分打印在单独的行上:

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);
    }
}

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