Разбор INI-файла в C - как хранить разделы и их ключи и значения? - PullRequest
0 голосов
/ 04 апреля 2020

Я пытаюсь разобрать файл .ini, используя только СТАНДАРТНЫЕ библиотеки в C. Входные файлы выглядят так:

[section1]
key1 = value1 
key2 = value2

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

я работаю с ./file inputfile.ini section2.key3 и я хочу получить значение ключа 3 из раздела 2

МОЙ ВОПРОС: Как легко хранить ключи и значения? - Я новичок, поэтому мне нужно что-то простое и легкое в реализации - может быть, структура, но как сохранить все ключи и значения внутри структуры, если я не знаю количество ключей?

Я застрял здесь, два раздел строк и 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;
}```

Ответы [ 2 ]

0 голосов
/ 04 апреля 2020

Вы работаете по правильному пути, но есть несколько вещей, к которым вы должны подходить по-разному, если вы хотите, чтобы все работало правильно. Если вы не получите ничего из этого ответа, узнайте, что вы не можете использовать любую функцию ввода или синтаксического анализа без проверки возврата (которая применяется практически к каждой используемой вами функции, кроме операции кода последующее не зависит от результата - как, например, просто печать значений) Кроме того, вы никогда не используете while (!feof(fpointer)), например, смотрите: Почему while (! feof (file)) всегда неверно?

Теперь, как подойти к проблеме. Во-первых, если вам нужна константа для размера вашего массива, тогда #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 и подтвердите разделение:

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

Теперь введите ваше чтение l oop, чтобы найти свой раздел в файле (вы всегда управляете l oop с помощью 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;
    }

Если вы не вышли из-за того, что не нашли свой раздел (то есть вы достигли этой точки в коде), просто прочитайте каждую строку проверка разделение на inikey и val (если проверка не пройдена - вы прочитали все пары ключ / val в этом разделе без совпадения) Если вы найдете совпадение ключей во время чтения раздела успеха у вас есть inikey и val. Если вы заполнили l oop без совпадения, вы можете проверить, выдаете ли вы ошибку, и если вы наберете EOF без совпадения, вы можете снова проверить feof(fp) после l oop, например,

    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.

Просмотрите все и сообщите мне, если у вас есть дополнительные вопросы.

0 голосов
/ 04 апреля 2020

Как вы указали, проблема в выражении if сравнения строк, дело в том, что переменная массива char (и любой массив в C) в действительности является указателем на первый элемент массива ( это объяснение слишком упрощено).

Таким образом, в своем выражении if вы действительно сравниваете адреса памяти первого элемента каждой из двух переменных массива вместо сравнения содержимого друг друга.

Для правильного сравнения строк есть несколько опций, вы можете сделать это вручную (перебирая каждый массив и сравнивая элемент за элементом), или вы можете использовать некоторые помощники из стандартной библиотеки, такие как strcmp или memcmp.

Например, вы можете переписать выражение if следующим образом:

#include <string.h>

if (memcmp ( section, current_section, sizeof(section) ) == 0) {
   // both arrays have the same content
}
...