【问题标题】:How to structure a good/flexible input file for C to read data如何为 C 构造一个好的/灵活的输入文件来读取数据
【发布时间】:2014-05-23 12:51:19
【问题描述】:

我很好奇如何编写 C 代码以灵活的方式读取输入文件。

作为一个非常简单的示例,假设我有变量 abcd。我可以按如下方式编写输入文件

 a = 1.0 
 b = 5.0
 c = 2.33
 d = 0.9

而且,如果我声明 abcd 的类型为 double,我可以简单地使用

 FILE *fr;
 // set some default values
 double a = 1.0, b = 1.0,
       c = 2.0, d = 2.0;

 fr = fopen("input_file.txt", "rt");

 fscanf(fr, "a = %lf b =  %lf c =  %lf d =  %lf", &a, &b, &c, &d);

 printf("%f %f %f %f\n",
        a, b, c, d);

读取数据。但这似乎很死板,例如abcd 在输入文件中必须完全按照这个顺序,所以制作输入文件

 a = 1.0 
 c = 5.0
 b = 2.33
 d = 0.9

不起作用,我无法为类似的东西添加 cmets

 a = 1.0   // This is parameter foo, must have properties P1, P2 and P3
 c = 5.0
 b = 2.33
 d = 0.9

当我有 20 个变量而不仅仅是 abcd 时,这一点尤其重要。任何使之尽可能灵活的帮助都会很棒。我在 Fortran 中使用了实现“namelist”的代码,这非常灵活,类似 C 语言的代码会很棒。

【问题讨论】:

  • 请记住,除非您实现它,否则 C 变量在运行时没有名称。
  • 嗨,对不起,我对此有点困惑。我声明变量abcd 并给它们一些默认值,但我希望用户修改输入文件中的值(而不是每次都重新编译)
  • 您可能需要一个诸如struct VarMap { const char *name; double *data; } var_map[] = { { "a", &a }, { "b", &b }, { "c", &c }, { "d", &d }, }; 之类的结构来将(运行时)名称映射到不同的变量。然后,您将使用格式中的" %s = %lf" 来读取名称(%s 允许使用多字符名称)和值,然后搜索 varmap 以查找放置数据的位置。
  • @JonathanLeffler,谢谢,这看起来很有希望,但我不得不承认我并没有完全遵循所有内容。我了解您定义的结构,但我不完全了解如何使用该结构从输入文件中读取。

标签: c scanf file-io


【解决方案1】:

转移和扩展评论。

您可能需要如下结构:

struct VarMap
{
    const char *name;
    double     *data;
} var_map[] =
{
    { "a", &a }, { "b", &b },
    { "c", &c }, { "d", &d },
};

将(运行时)名称映射到不同的变量。然后,您将使用格式中的"%s = %lf" 来读取名称(%s 允许使用多字符名称)和值,然后搜索varmap 以查找放置数据的位置。

enum { NUM_VARS = sizeof(varmap) / sizeof(varmap[0]) };

char name[10];
double value;

if (fscanf(fr, "%9s = %lf", name, &value) == 2)
{
    int i;
    for (i = 0; i < NUM_VARS; i++)
    {
        if (strcmp(name, varmap[i].name) == 0)
        {
            *varmap[i].data = value;
            break;
        }
    }
    if (i == NUM_VARS)
    {
        fprintf(stderr, "Failed to find match for '%s = %g'\n", name, value);
        ...other error handling?...
    }
}

很明显,如果您有大量变量,您将需要考虑对名称进行二分搜索甚至是基于哈希的搜索,然后将搜索抽象为一个函数,而不是像上面的代码那样内联编写它。

varmap 的初始化程序存在一个限制——在大多数情况下,指向的名称和变量在编译时是固定的。另一种机制是使用命名变量数组:

struct NamedVar
{
    const char *name;
    double      value;
};

您可以在结构中使用固定大小的char 数组,或者将名称变成灵活的数组成员。

struct NamedVar
{
    double value;
    char   name[];
};

然后,您可以动态分配 struct NamedVar 值,将指向它们的指针保存在数组中,按需添加新的命名变量。

您需要一个函数double named_variable(const char *name) 来获取值,并需要void set_named_variable(const char *name, double value) 来设置值。哪种方法最好取决于谁可以设计变量名以及您必须处理多少个变量名。对于 4 个变量,或 40 个变量,struct VarMap 方法是合理的;对于 400 个变量或 4000 个变量,这真的没有意义,struct NamedVar 方法可能更好。

【讨论】:

    【解决方案2】:

    大多数配置文件格式需要解析文件,用其中包含的数据填充一些通用结构,然后处理这些结构以找到与您想要的相关联的值。

    查看此 stackoverflow 链接以获取有关使用 XML 配置解析库的更多信息:

    XML Parser for C

    最好的办法是使用 INI、XML 或 JSON 文件,因此另一种调查途径是寻找 C 语言的解析器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-16
      相关资源
      最近更新 更多