从文件 w/period 中读取数字(整数和小数)作为分隔符

Reading Numbers (integers and decimals) from file w/period as delimiter

目前,我想获取带有分隔符 . 的文件。但是,使用fscanf无法区分小数和整数。

想要提取前两个整数(由 . 分隔)和最后的小数到 tmp[]

尝试 fscanf(file, "%d.", &tmp[j]);%d%d.%f,都没有用。


%d 的结果:tmp[3]: {1.61149323e-43, 0, 0.409999996}

%d. 的结果:tmp[3]: {1.61149323e-43, 5.7453237e-44, 24.3600006}

%f 的结果:tmp[3]: {115.410004, 24.3600006, 113.489998}

%d%d. [0] 和 [1] 出现乱码,只有 %d. [3] 似乎没看错。
%f 看起来不错,但它把前两个数字融合成一位小数。

我必须使用 fgets() 还是 fgetc()?或者我可以更改 fscanf() 格式说明符以使其正确?


预期结果:(在调试器中)

tmp[0]: 115
tmp[1]: 41
tmp[2]: 24.36

(file line 2 as input)
tmp[0]: 113
tmp[1]: 49
tmp[2]: 44

来源:

#include <stdio.h>
#include <stdlib.h>
int main() {
    FILE *file = fopen("data.txt", "r");
    float tmp[3] = {};
        for(int j = 0; j < 2; j++) {
            fscanf(file, "%d.", &tmp[j]);
        }
        fscanf(file, "%f", &tmp[2]);
} 

Data.txt

115.41.24.360
113.49.44
115.41.51.573

%d 转换规范用于读取十进制整数,需要一个 int * 参数。同样,%i%o%u%x%X 转换规范也适用于整数。

使用%f是行不通的;它会将 'second integer' 读取为第一个 floating-point 值的小数部分,并且没有办法阻止它这样做(除非你可以安全地使用 %3f 因为总会有大多数 3 位数——这是一个可疑的命题。

您需要使用

int i1, i2;
if (fscanf(file, "%d.%d.%f", &i1, &i2, &tmp[2]) != 3)
{
    …handle error…
}
tmp[0] = i1;
tmp[1] = i2;

这会将整数值读入 int 变量,然后将结果分配给 float 数组元素。请注意,您应该始终检查 fscanf() 和朋友的 return 值。如果它不是你所期望的,那就有问题(只要你的期望是正确的,一旦你经常阅读 fscanf() 的规范,它们就会是这样——第一次每天几次几天,下周每天一次,下个月每周一次,明年大约每月一次)。

每当您必须将多种类型作为单个对象处理时,您应该考虑 struct 提供存储。这里有两个整数和一个要存储的浮点数。允许这样做的简单结构是:

typedef struct {        /* struct to hold data from line */
  int n1, n2;
  float f;
} twointf;

typedef 是为了方便,让您可以引用 twointf 作为类型,而不是每次都重复 struct twointf。使用结构,您现在可以声明一个简单的 twointf 数组来处理所需数量的数组。

要读取数据,使用 fgets() 将整行读入足够大小的缓冲区(字符数组)并使用 sscanf() 进行解析总是优于使用 [=21= 直接读取].如果您的数据文件中有错误,fgets() 会将整行读入缓冲区,错误只会影响单行数据。使用 fscanf() 可能会发生 Matching-Failure,或者您的读取将换行到下一行,其中省略的值会破坏您从该点向前读取的文件。

要用 fgets() 阅读并用 sscanf() 分开,你可以这样做:

#define MAXC 1024       /* if you need a constant, #define one (or more) */
#define MAXS   32
...
    char buf[MAXC];                       /* read buffer for line */
    size_t ndx = 0;                       /* array of struct index */
    twointf arr[MAXS] = {{ .n1 = 0 }};    /* array of struct */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    ...
    /* while array not full, read line */
    while (ndx < MAXS && fgets (buf, MAXC, fp)) {
      if (sscanf (buf, "%d.%d.%f",    /* separate into 2 int and float */
                  &arr[ndx].n1, &arr[ndx].n2, &arr[ndx].f) == 3) {
        ndx++;    /* on success, increment index */
      }
    }

仅此而已。读取您的数据,使用 sscanf() 进行转换以验证 return 并且仅在成功转换所有值时递增数组索引。

总而言之,你可以做到:

#include <stdio.h>

#define MAXC 1024       /* if you need a constant, #define one (or more) */
#define MAXS   32

typedef struct {        /* struct to hold data from line */
  int n1, n2;
  float f;
} twointf;

int main (int argc, char **argv) {
    
    char buf[MAXC];                       /* read buffer for line */
    size_t ndx = 0;                       /* array of struct index */
    twointf arr[MAXS] = {{ .n1 = 0 }};    /* 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, read line */
    while (ndx < MAXS && fgets (buf, MAXC, fp)) {
      if (sscanf (buf, "%d.%d.%f",    /* separate into 2 int and float */
                  &arr[ndx].n1, &arr[ndx].n2, &arr[ndx].f) == 3) {
        ndx++;    /* on success, increment index */
      }
    }
    
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
    
    for (size_t i = 0; i < ndx; i++)  /* output results */
        printf ("\narr[%zu].n1 = %d\narr[%zu].n2 = %d\narr[%zu].f  = %.2f\n",
                i, arr[i].n1, i, arr[i].n2, i, arr[i].f);
}

例子Use/Output

使用 dat/data_int_float.txt 中的示例数据,您将:

$ ./bin/twointf dat/data_int_float.txt

arr[0].n1 = 115
arr[0].n2 = 41
arr[0].f  = 24.36

arr[1].n1 = 113
arr[1].n2 = 49
arr[1].f  = 44.00

arr[2].n1 = 115
arr[2].n2 = 41
arr[2].f  = 51.57

(如果存在小数数据,您可以使用 %g 格式说明符 floating-point 仅输出小数位 - 由您决定)