在 C 中解析 INI 文件 - 如何存储部分及其键和值?
Parsing INI file in C - how to store sections and its' keys and values?
我正在尝试仅使用 C 中的标准库来解析 .ini 文件。
输入文件如下所示:
[section1]
key1 = value1
key2 = value2
[section2]
key3 = vaule3
key4 = value4
key5 = value5
...
im 运行 与 ./file inputfile.ini section2.key3
和 我想从 section2
获取 key3 的值
我的问题是:如何轻松地存储键和值? - 我是一个初学者,所以我需要一些简单易行的东西 - 也许是结构,但如果我不知道键的数量,如何将所有键和值存储在结构中?
我卡在这里了,两个字符串部分和 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;
}```
正如您所指出的,问题出在字符串比较 if
语句中,问题在于 char 数组(以及 C 中的任何数组)变量实际上是指向第一个元素的指针数组(这个解释过于简单化)。
因此,在您的 if
语句中,您实际上是在比较两个数组变量中每一个的第一个元素的内存地址,而不是相互比较内容。
要正确比较字符串,有多种选择,您可以手动进行(遍历每个数组并逐个元素比较),或者您可以使用标准库中的一些帮助程序,例如 strcmp
或 memcmp
.
例如,您可以如下重写 if 语句:
#include <string.h>
if (memcmp ( section, current_section, sizeof(section) ) == 0) {
// both arrays have the same content
}
您正在沿着正确的道路前进,但如果您想确保事情正常进行,则必须以不同的方式处理一些事情。如果您从这个答案中没有得到任何其他信息,请了解如果没有 检查 return(适用几乎每个你使用的函数,除非后面的代码操作不依赖于结果——比如只打印值)另外,你从不使用 while (!feof(fpointer))
,例如参见:Why is while ( !feof (file) ) always wrong?
现在,如何解决这个问题。首先,如果你的数组大小需要一个常量,那么 #define
常量或使用全局 enum
。例如,对于我的 sect
、inisect
、key
、inikey
和 val
缓冲区,我将定义 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]
拆分为 sect
和 key
以及 validate分离:
/* 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 分离为 inikey
和 val
(如果验证失败——你已经阅读了该部分中所有没有匹配的 key/val 对)如果你在期间找到关键匹配您阅读了 成功 部分,您有 inikey
和 val
。如果你在没有匹配的情况下完成循环,你可以检查你是否发出错误,如果你到达 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;
}
}
例子Use/Output
找到有效的 section/key 组合:
$ ./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
尝试找到无效的 section/key 组合。
$ ./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.
检查一下,如果您还有其他问题,请告诉我。
我正在尝试仅使用 C 中的标准库来解析 .ini 文件。 输入文件如下所示:
[section1]
key1 = value1
key2 = value2
[section2]
key3 = vaule3
key4 = value4
key5 = value5
...
im 运行 与 ./file inputfile.ini section2.key3
和 我想从 section2
我的问题是:如何轻松地存储键和值? - 我是一个初学者,所以我需要一些简单易行的东西 - 也许是结构,但如果我不知道键的数量,如何将所有键和值存储在结构中?
我卡在这里了,两个字符串部分和 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;
}```
正如您所指出的,问题出在字符串比较 if
语句中,问题在于 char 数组(以及 C 中的任何数组)变量实际上是指向第一个元素的指针数组(这个解释过于简单化)。
因此,在您的 if
语句中,您实际上是在比较两个数组变量中每一个的第一个元素的内存地址,而不是相互比较内容。
要正确比较字符串,有多种选择,您可以手动进行(遍历每个数组并逐个元素比较),或者您可以使用标准库中的一些帮助程序,例如 strcmp
或 memcmp
.
例如,您可以如下重写 if 语句:
#include <string.h>
if (memcmp ( section, current_section, sizeof(section) ) == 0) {
// both arrays have the same content
}
您正在沿着正确的道路前进,但如果您想确保事情正常进行,则必须以不同的方式处理一些事情。如果您从这个答案中没有得到任何其他信息,请了解如果没有 检查 return(适用几乎每个你使用的函数,除非后面的代码操作不依赖于结果——比如只打印值)另外,你从不使用 while (!feof(fpointer))
,例如参见:Why is while ( !feof (file) ) always wrong?
现在,如何解决这个问题。首先,如果你的数组大小需要一个常量,那么 #define
常量或使用全局 enum
。例如,对于我的 sect
、inisect
、key
、inikey
和 val
缓冲区,我将定义 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]
拆分为 sect
和 key
以及 validate分离:
/* 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 分离为 inikey
和 val
(如果验证失败——你已经阅读了该部分中所有没有匹配的 key/val 对)如果你在期间找到关键匹配您阅读了 成功 部分,您有 inikey
和 val
。如果你在没有匹配的情况下完成循环,你可以检查你是否发出错误,如果你到达 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;
}
}
例子Use/Output
找到有效的 section/key 组合:
$ ./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
尝试找到无效的 section/key 组合。
$ ./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.
检查一下,如果您还有其他问题,请告诉我。