【问题标题】:Parsing INI file in C - how to store sections and its' keys and values?在 C 中解析 INI 文件 - 如何存储部分及其键和值?
【发布时间】:2020-07-16 05:41:17
【问题描述】:

我正在尝试仅使用 C 中的标准库来解析 .ini 文件。 输入文件如下所示:

[section1]
key1 = value1 
key2 = value2

[section2]
key3 = vaule3
key4 = value4
key5 = value5
...

我用./file inputfile.ini section2.key3 运行它,我想从 section2 获取 key3 的值

我的问题是:如何轻松存储键和值? - 我是一个初学者,所以我需要一些简单且易于实现的东西 - 也许是 struct 但如果我不知道键的数量,如何将所有键和值存储在 struct 中?

我卡在这里,两个字符串部分和 current_section 看起来相同,但在 if(section == current_section) 他们没有通过 True,有什么问题?

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


int main(int argc, char *argv[]) {

    FILE * fPointer;
    fPointer = fopen(argv[1], "r");  // read from file

    char singleLine[30];
    char section[30];
    char key[30];
    int right_section = 0;
    char current_section[30];

    sscanf(argv[2], "%[a-zA-Z0-9].%[a-zA-Z0-9]", section, key); //split of section.key

    while(!feof(fPointer)){
        fgets(singleLine, 30, fPointer); //read line by line
        printf("%s", singleLine);
        char current_key[30];
        char current_value[30];

        if(singleLine[0]=='['){
            sscanf(singleLine, "[%127[^]]", current_section); //strip from []
            printf("current section:%s%s", current_section, section); //both look equally
            if(current_section == section){ // doesn't work here, current_section == section looks the same but if doesnt work
                right_section = 1;
                printf("yes, right");
            }
        }

    }

    fclose(fPointer);

    return 0;
}```

【问题讨论】:

  • 这能回答你的问题吗? INI file parser for C
  • 好吧,只需将带有fgets() 的每一行读入一个足够大的字符数组(不要吝啬大小),比如buf,然后检查buf[0] == '['(或只是*buf == '[' ),如果是这样,则使用sscanf() 将部分名称读入另一个数组,如果不是您的部分,请继续阅读直到下一个'[',直到找到您的部分,然后使用fgets() 读取,使用sscanf() 分隔成@987654334 @ 和 value 字符串,直到您匹配您的搜索条件。
  • @DavidC.Rankin 谢谢伙计,我没有这样想。你能告诉我如何存储每个部分及其键和值吗?我应该使用什么,那会是什么样子?我知道我不需要为我的问题存储它们,但我想知道
  • 好吧,首先将您的输入作为./file inputfile.ini section2 key3 并省去拆分section2.key3 会更有意义。然后我会声明char buf[256], sect[128], key[128], value[128]; 打开您的文件并验证它是否已打开以供阅读。然后while (fgets (buf, sizeof buf, fp)) { ... 读取每一行测试if (buf[0] == '[') { ... 找到每个部分的开始。然后您可以阅读带有if (sscanf (buf, " [%127[^]]", sect) == 1) 的部分并测试if (strcmp (sect, argv[2]) == 0) 以查看您是否匹配您的部分。然后使用sscanf 拆分键/值。
  • 如果您遇到困难,请编辑您的问题并将您目前所拥有的内容添加到问题的末尾,我很乐意为您提供进一步的帮助。对于一个简单的方法,您可以循环查找您的部分,break 当您用正确的部分填充 sect 变量时循环。现在你可以检查if (feof(fp)) { printf ("error: end of section '%s' reached with no matching key found.\n", sect); return 1; ) 然后你可以进入你的第二个循环来匹配keyval。要解析 keyvalsscanf() 格式字符串将类似于 " %127s = %127s"

标签: c ini


【解决方案1】:

您正在沿着正确的道路前进,但是如果您想确保事情正常进行,您必须采取不同的方法。如果您从这个答案中没有得到任何其他信息,请了解您不能在没有检查返回的情况下使用任何输入或解析函数(这几乎适用于您使用的每个函数,除非操作后面的代码不依赖于结果——就像打印值一样)另外,你从不使用while (!feof(fpointer)),例如见:Why is while ( !feof (file) ) always wrong?

现在,如何解决这个问题。首先,如果您的数组大小需要一个常量,那么#define 是一个常量或使用全局enum。例如,对于我的 sectinisectkeyinikeyval 缓冲区,我将定义 SPLTC,然后对于我的行缓冲区,我定义 MAXC,例如

#define SPLTC 128       /* if you need a constant, #define one (or more) */
#define MAXC  256

根据您是否需要与 -ansi 或 c89/90 兼容,在任何操作之前声明您的变量,例如

int main (int argc, char **argv) {

    char buf[MAXC], sect[SPLTC], inisect[SPLTC], key[SPLTC], inikey[SPLTC], val[SPLTC];
    FILE *fp = NULL;

那么你要做的第一件事就是验证命令行提供了足够的参数:

    if (argc < 3) { /* validate 2 arguments provided */
        fprintf (stderr,
                "error: insufficient number of arguments\n"
                "usage: %s file.ini section.key\n", argv[0]);
        return 1;
    }

接下来,您将打开您的文件并验证它已打开以供阅读:

    /* open/validate file open for reading */
    if ((fp = fopen (argv[1], "r")) == NULL) {
        fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
        perror ("fopen");
        return 1;
    }

然后将您的section.key 参数argv[2] 拆分为sectkey验证分离:

    /* split section.key into sect & key */
    if (sscanf (argv[2], " %127[^.]. %127s", sect, key) != 2) {
        fputs ("error: invalid section.key\n", stderr);
        return 1;
    }

现在进入您的读取循环以在文件中找到您的部分(您始终使用读取函数本身的 return 控制循环):

    while (fgets (buf, MAXC, fp)) {                 /* read each line */
        if (buf[0] == '[') {                        /* is first char '[]'? */
            if (sscanf (buf, " [%127[^]]", inisect) == 1) { /* parse section */
                if (strcmp (sect, inisect) == 0)    /* does it match 2nd arg? */
                    break;                          /* if so break loop */
            }
        }
    }

您如何检查是否找到了该部分?您可以像使用right_section 一样保留标志变量,或者...想想如果找不到您的部分,您将在文件中的哪个位置?你会在EOF。所以现在你可以正确检查feof(fp),例如

    if (feof (fp)) {    /* if file stream at EOF, section not found */
        fprintf (stderr, "error: EOF encountered before section '%s' found.\n", 
                sect);
        return 1;
    }

如果你因为没有找到你的部分而没有退出(意味着你已经到了代码中的这一点),只需将每一行 validating 读入@ 987654347@ 和 val (如果验证失败 - 您已阅读该部分中的所有键/值对,但未匹配)如果您在阅读 success 部分期间找到键匹配项有你的inikeyval。如果你在没有匹配的情况下完成循环,你可以检查你是否发出错误,如果你没有匹配到EOF,你可以在循环之后再次检查feof(fp),例如

    while (fgets (buf, MAXC, fp)) {                 /* continue reading lines */
        /* parse key & val from line */
        if (sscanf (buf, " %127s = %127s", inikey, val) != 2) { /* if not key & val */
            fprintf (stderr, "error: end of section '%s' reached "
                    "with no matching key found.\n", sect);
            return 1;
        }
        if (strcmp (key, inikey) == 0) {           /* does key match? */
            printf ("section : %s\n  key : %s\n  val : %s\n", sect, key, val);
            break;
        }
    }

    if (feof (fp)) {    /* if file stream at EOF, key not found */
        fprintf (stderr, "error: EOF encountered before key '%s' found.\n", 
                argv[3]);
        return 1;
    }

基本上就是这样。如果你把它放在一起,你有:

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

#define SPLTC 128       /* if you need a constant, #define one (or more) */
#define MAXC  256

int main (int argc, char **argv) {

    char buf[MAXC], sect[SPLTC], inisect[SPLTC], key[SPLTC], inikey[SPLTC], val[SPLTC];
    FILE *fp = NULL;

    if (argc < 3) { /* validate 2 arguments provided */
        fprintf (stderr,
                "error: insufficient number of arguments\n"
                "usage: %s file.ini section.key\n", argv[0]);
        return 1;
    }

    /* open/validate file open for reading */
    if ((fp = fopen (argv[1], "r")) == NULL) {
        fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
        perror ("fopen");
        return 1;
    }

    /* split section.key into sect & key */
    if (sscanf (argv[2], " %127[^.]. %127s", sect, key) != 2) {
        fputs ("error: invalid section.key\n", stderr);
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {                 /* read each line */
        if (buf[0] == '[') {                        /* is first char '[]'? */
            if (sscanf (buf, " [%127[^]]", inisect) == 1) { /* parse section */
                if (strcmp (sect, inisect) == 0)    /* does it match 2nd arg? */
                    break;                          /* if so break loop */
            }
        }
    }

    if (feof (fp)) {    /* if file stream at EOF, section not found */
        fprintf (stderr, "error: EOF encountered before section '%s' found.\n", 
                sect);
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {                 /* continue reading lines */
        /* parse key & val from line */
        if (sscanf (buf, " %127s = %127s", inikey, val) != 2) { /* if not key & val */
            fprintf (stderr, "error: end of section '%s' reached "
                    "with no matching key found.\n", sect);
            return 1;
        }
        if (strcmp (key, inikey) == 0) {           /* does key match? */
            printf ("section : %s\n  key : %s\n  val : %s\n", sect, key, val);
            break;
        }
    }

    if (feof (fp)) {    /* if file stream at EOF, key not found */
        fprintf (stderr, "error: EOF encountered before key '%s' found.\n", 
                argv[3]);
        return 1;
    }
}

使用/输出示例

查找有效的部分/组合键:

$ ./bin/readini dat/test.ini section2.key3
section : section2
  key : key3
  val : vaule3

$ /bin/readini dat/test.ini section2.key5
section : section2
  key : key5
  val : value5

$ ./bin/readini dat/test.ini section1.key2
section : section1
  key : key2
  val : value2

尝试查找无效的部分/键组合。

$ ./bin/readini dat/test.ini section1.key3
error: end of section 'section1' reached with no matching key found.

$ ./bin/readini dat/test.ini section2.key8
error: EOF encountered before key 'key8' found.

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

【讨论】:

    【解决方案2】:

    正如您所指出的,问题出在字符串比较 if 语句中,问题是 char 数组(以及 C 中的任何数组)变量实际上是指向数组第一个元素的指针(这个解释过于简单)。

    因此,在您的 if 语句中,您实际上是在比较两个数组变量中每个变量的第一个元素的内存地址,而不是比较彼此的内容。

    为了对字符串进行正确比较,有几个选项,您可以手动进行(遍历每个数组并逐个元素进行比较),也可以使用标准库中的一些帮助程序,例如 strcmpmemcmp .

    例如你可以重写你的 if 语句如下:

    #include <string.h>
    
    if (memcmp ( section, current_section, sizeof(section) ) == 0) {
       // both arrays have the same content
    }
    

    【讨论】:

      猜你喜欢
      • 2016-04-10
      • 2015-10-18
      • 2016-04-25
      • 2015-08-09
      • 2017-06-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-05
      • 2013-02-17
      相关资源
      最近更新 更多