【问题标题】:variable definition by fscanf textfile.txt in CC 中 fscanf textfile.txt 的变量定义
【发布时间】:2020-06-21 12:21:13
【问题描述】:

我有一个 textfile.txt 逐行包含变量定义,例如长度 = 1.1m

使用fscanf 我想在我的变量double length 中存储值1.1

谁能给我一些提示,在这种情况下如何使用 fscanf 有一个 '=' 分隔符?

提前致谢!

编辑: 下面是一个示例 inputfile.txt,显示了它的结构:

[elements]
Number of elements = 2
length_1 = 1.5m
length_2 = 2.1m
velocity_1 = 0.35m/s
velocity_2 = 0.11m/s

[cells]
Number of cells = 2
cell_1 = 0.0m
cell_2 = 1.3m

我想将两个部分的参数提取到两个不同的结构中。

【问题讨论】:

  • 是的,不要使用fscanf()。使用fgets()strtok(),最后使用sscanf()strtod()
  • 如果表单总是像 length = 1.1m 那样表示变量的单词(没有空格或制表符的字符串,换行符..),然后是空格,然后是 =,然后是值,然后是单词为团结。所以fscanf的格式就很明显了
  • 您不能将字符串"length" 转换为变量名。您必须已经拥有一个该名称,或者,使用 id 名称和值构建一个 struct。如果您还有"width = 2.3m" 之类的定义,那么您可能希望将数据提取到其他特定变量或数组中。无论如何,问题定义太宽泛了。
  • 感谢@bruno,我现在可以使用:fscanf(file, "%*s %*s %s ", word)==1 和单词是 ````word = char[100]``` 来读取值,从而提取值 + 统一。有没有办法省略统一(我有不同统一的变量,例如 m,m/s ,...)
  • 除了“var”名称可以包含空格之外,表单仍然是<name><space(s)>=<value><optional unity>,您可以管理它。如果您想要一个完全有效的解决方案,您需要解释所有可能在您的输入文件中找到的内容,您想要提取/保存的内容,编辑您的问题

标签: c scanf


【解决方案1】:

一个读取格式化文件的简单程序:

#include <stdio.h>

int main(void) {
    FILE *fp = fopen("data.txt", "r");
    float length;

    if (fp == NULL) {
        printf("The file was failed to open.\n");
        return -1;
    }

    while (fscanf(fp, "%*s %*s %f", &length) == 1)
        printf("%f", length);

    return 0;
}

注意我们已经使用了

%*s %*s %f

format %*s string 跳过该单词的阅读。

文件data.txt 看起来像:

length = 5.324325m

然后你会得到一个输出:

5.324325

您现在可以清楚地看到字符串被截断并且值已成功分配给变量。

【讨论】:

  • 如何提供一个解决方案,同时保存 var 名称和统一性,以及另一个解决方案,即使在 '=' 之前没有空格也可以工作?
【解决方案2】:

这里有一个办法,两个部分可以任意顺序给出,也可以只给出一个,甚至不给出:

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

typedef struct Element {
  double length;
  double velocity;
} Element;

void * read_section(FILE * fp, const char * what, int * n, size_t sz)
{
  if (*n != 0) {
    fprintf(stderr, "invalid file, several sections [%s]\n", what);
    exit(-1);
  }

  char fmt[64];
  
  sprintf(fmt, " Number of %s = %%d", what); /* notice space at beginning */

  if (fscanf(fp, fmt, n) != 1) {
    fprintf(stderr, "invalid file, expected 'Number of %s = <n>'\n", what);
    exit(-1);
  }
  
  if (*n <= 0) {
    fprintf(stderr, "number of %s must be > 0\n", what);
    exit(-1);
  }
  
  void * r = malloc(*n * sz);
  
  if (r == NULL) {
    fprintf(stderr, "not enough memory for %d %s\n", *n, what);
    exit(-1);
  }
  
  return r;
}

void read_var(FILE * fp, const char * var, const char * unity, int rank, double * v)
{
  char fmt[64];
  
  sprintf(fmt, " %s_%d = %%lg%s", var, rank, unity); /* notice space at beginning */
  
  if (fscanf(fp, fmt, v) != 1) {
    fprintf(stderr, "invalid file, expected '%s_%d = <val>%s'\n", var, rank, unity);
    exit(-1);
  }
}

int main(int argc, char ** argv)
{
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <file>\n", *argv);
    exit(-1);
  }
  
  FILE * fp = fopen(argv[1], "r");
  
  if (fp == NULL) {
    perror("cannot open file");
    exit(-1);
  }
  
  int nelts = 0;
  Element * elts = NULL;
  int ncells = 0;
  double * cells = NULL;
  char line[32];
  int i;
  
  while (fscanf(fp, " %31s", line) == 1) { /* notice space at beginning of format */
    if (!strcmp(line, "[elements]")) {
      elts = read_section(fp, "elements", &nelts, sizeof(*elts));
      for (i = 0; i != nelts; ++i)
        read_var(fp, "length", "m", i+1, &elts[i].length);
      for (i = 0; i != nelts; ++i)
        read_var(fp, "velocity", "m/s", i+1, &elts[i].velocity);
    }
    else if  (!strcmp(line, "[cells]")) {
      cells = read_section(fp, "cells", &ncells, sizeof(*cells));
      for (i = 0; i != ncells; ++i)
        read_var(fp, "cell", "m", i+1, &cells[i]);
    }
    else {
      fputs("invalid file, section header expected\n", stderr);
      exit(-1);
    }
  }
  
  fclose(fp);
  
  /* to check */
  
  printf("%d elements:\n", nelts);
  for (i = 0; i != nelts; ++i)
    printf("%d) length=%g, velocity=%g\n", i, elts[i].length, elts[i].velocity);
  
  printf("\n%d cells:", ncells);
  for (i = 0; i != ncells; ++i)
    printf(" %g", cells[i]);
  putchar('\n');
  
  free(elts);
  free(cells);
  
  return 0;
}

当需要绕过换行符时,请注意格式开头的空格(以及可能的其他空格)。

用你的例子编译和执行:

pi@raspberrypi:/tmp $ gcc -g -Wall x.c
pi@raspberrypi:/tmp $ 
pi@raspberrypi:/tmp $ cat f1
[elements]
Number of elements = 2
length_1 = 1.5m
length_2 = 2.1m
velocity_1 = 0.35m/s
velocity_2 = 0.11m/s

[cells]
Number of cells = 2
cell_1 = 0.0m
cell_2 = 1.3m
pi@raspberrypi:/tmp $ ./a.out f1
2 elements:
0) length=1.5, velocity=0.35
1) length=2.1, velocity=0.11

2 cells: 0 1.3
pi@raspberrypi:/tmp $ 

交换部分顺序

pi@raspberrypi:/tmp $ cat f2
[cells]
Number of cells = 2
cell_1 = 0.0m
cell_2 = 1.3m
[elements]
Number of elements = 2
length_1 = 1.5m
length_2 = 2.1m
velocity_1 = 0.35m/s
velocity_2 = 0.11m/s
pi@raspberrypi:/tmp $ ./a.out f2
2 elements:
0) length=1.5, velocity=0.35
1) length=2.1, velocity=0.11

2 cells: 0 1.3
pi@raspberrypi:/tmp $ 

只有元素

pi@raspberrypi:/tmp $ cat f3
[elements]
Number of elements = 2
length_1 = 1.5m
length_2 = 2.1m
velocity_1 = 0.35m/s
velocity_2 = 0.11m/s

pi@raspberrypi:/tmp $ ./a.out f3
2 elements:
0) length=1.5, velocity=0.35
1) length=2.1, velocity=0.11

0 cells:
pi@raspberrypi:/tmp $ 

只有单元格

pi@raspberrypi:/tmp $ cat f4
[cells]
Number of cells = 2
cell_1 = 0.0m
cell_2 = 1.3m
pi@raspberrypi:/tmp $ ./a.out f4
0 elements:

2 cells: 0 1.3
pi@raspberrypi:/tmp $ 

空输入文件

pi@raspberrypi:/tmp $ ./a.out /dev/null
0 elements:

0 cells:
pi@raspberrypi:/tmp $ 

更多元素和单元格

pi@raspberrypi:/tmp $ cat f
[elements]
Number of elements = 3
length_1 = 1.5m
length_2 = 2.1m
length_3 = 1.1m
velocity_1 = 0.35m/s
velocity_2 = 0.11m/s
velocity_3 = 0.33m/s

[cells]
Number of cells = 5
cell_1 = 0.0m
cell_2 = 1.3m
cell_3 = 2.3m
cell_4 = 3.3m
cell_5 = 4.3m
pi@raspberrypi:/tmp $ ./a.out f
3 elements:
0) length=1.5, velocity=0.35
1) length=2.1, velocity=0.11
2) length=1.1, velocity=0.33

5 cells: 0 1.3 2.3 3.3 4.3
pi@raspberrypi:/tmp $ 

并且检查了可能的错误情况,我鼓励您永远不要假设一切都正常,但要检查它

如果您在 Linux/Unix 下完成,请检查您在 valgrind 下运行的程序,例如:

pi@raspberrypi:/tmp $ valgrind ./a.out f
==13981== Memcheck, a memory error detector
==13981== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==13981== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==13981== Command: ./a.out f
==13981== 
3 elements:
0) length=1.5, velocity=0.35
1) length=2.1, velocity=0.11
2) length=1.1, velocity=0.33

5 cells: 0 1.3 2.3 3.3 4.3
==13981== 
==13981== HEAP SUMMARY:
==13981==     in use at exit: 0 bytes in 0 blocks
==13981==   total heap usage: 5 allocs, 5 frees, 5,560 bytes allocated
==13981== 
==13981== All heap blocks were freed -- no leaks are possible
==13981== 
==13981== For lists of detected and suppressed errors, rerun with: -s
==13981== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
pi@raspberrypi:/tmp $ 

【讨论】:

    【解决方案3】:

    假设你的文本文件是这样的:

    length = 1.2m
    length = 3.4m
    length = 12.4m
    

    然后你就可以用这段代码来阅读了:

    FILE* filePtr = fopen("textfile.txt","r"); 
    if (filePtr==NULL) 
    { 
        printf("no such file."); 
        return 0; 
    } 
      
    float num; 
    while (fscanf(filePtr,"length = %f", &num)==1) 
        printf("%f\n", num);
    fclose(filePtr);
    

    有关从文件读取的更多信息,请查看link

    【讨论】:

    • 假设在值之前总是length = 是一个很大的假设...
    • @bruno 我认为该文件是理想的,并且以我在第一个答案中所说的形式(为了保持答案简单),但你是对的,我们应该注意这一点
    • 对我来说,在“完美”文件之外,您不能假设 var 名称始终是“长度”,因为该声明谈到了 variable definitions(复数)。因此,我鼓励您编辑您的答案,以提出额外的方法来处理您对文件内容所做的相关假设。 OP也想要double而不是float
    • 感谢迄今为止提供建议的所有人,太好了!我已经编辑了上面的原始问题,显示了一个示例 inputfile.txt。请注意,我有不同的部分以及两种类型的变量格式。我可以以某种方式过滤 fscanf 部分 [elements] 中的行,然后将这些值存储在我的结构元素中,然后转到部分 [cells] 并做同样的事情吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多