Как обрабатывать <form>данных, полученных http-сервером, например (ключ = значение & ключ = значение & ключ = значение & ...), используя язык C, и назначать каждое значение переменной? - PullRequest
0 голосов
/ 15 января 2019

У меня http-сервер работает на микроконтроллере. Он обслуживает короткую HTML-страницу, которая содержит форму. После того, как я заполнил форму и нажал кнопку отправки, используя метод POST, я получил следующие значения формы:

Key1=value1&Key2=value2&Key3=value3&...

Все полученные данные сохраняются в виде строки внутри буфера.

Вопрос в том, как мне обработать эти данные, сохранив каждую переменную key = vale. например:

int key1 = value1 int key2 = value2 int key3 = value3

Большое спасибо

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Это предварено моими главными комментариями.

Вот полная реализация с тестовыми примерами:

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

enum {
    KEY1,
    KEY2,
    KEY3
};

typedef struct keyPlusValue {
    char *key;
    int value;
} keyPlusValue_t;

keyPlusValue_t symlist[] = {
    [KEY1] = { "key1", 0 },
    [KEY2] = { "key2", 0 },
    [KEY3] = { "key3", 0 },
};

int symcount = sizeof(symlist) / sizeof(symlist[0]);

void
parse_list(const char *str)
{
    char buf[strlen(str) + 1];
    char *bp;
    char *tok;
    char *val;
    keyPlusValue_t *symtry;
    keyPlusValue_t *symok;

    // NOTE: we _must_ create a copy of the string because caller could pass
    // in a constant string and we are going to _write_ into our copy
    strcpy(buf,str);

    bp = buf;
    while (1) {
        // get next "key=val" pair
        tok = strtok(bp,"&");
        if (tok == NULL)
            break;
        bp = NULL;

        // split up pair into "key" and "value"
        val = strchr(tok,'=');
        if (val == NULL) {
            printf("malformed token -- '%s'\n",tok);
            break;
        }
        *val++ = 0;

        // scan symbol/key table looking for match
        symok = NULL;
        for (symtry = symlist;  symtry < &symlist[symcount];  ++symtry) {
            if (strcmp(tok,symtry->key) == 0) {
                symok = symtry;
                break;
            }
        }

        // if no match found -- should not happen but _must_ be checked for
        if (symok == NULL) {
            printf("unknown key -- '%s'\n",tok);
            break;
        }

        // convert text representation of number into int
        symok->value = atoi(val);
    }
}

void
test(const char *str)
{
    keyPlusValue_t *sym;

    printf("\n");
    printf("test: '%s'\n",str);

    parse_list(str);

    for (sym = symlist;  sym < &symlist[symcount];  ++sym)
        printf(" key='%s' val=%d\n",sym->key,sym->value);
}

int
main(void)
{

    test("key1=1&key2=2&key3=3");
    test("key1=2&key2=3&key3=4");
    test("key1=3&key2=4&key3=5");

    return 0;
}
0 голосов
/ 15 января 2019

Если существует только фиксированный набор ключей, которые вас когда-либо интересуют, то вам также нужна таблица, которая связывает ключи с соответствующими переменными. Как пример:

enum { Key1, Key2, Key3 /* ... */};
struct {
    const char *key;
    int value;
} key_table[KEY_TABLE_SZ] = {
    [Key1] = { "Key1", INT_MIN },
    [Key2] = { "Key2", INT_MIN },
    [Key3] = { "Key3", INT_MIN },
    /* ... */
};

Когда вы анализируете строку параметров запроса и идентифицируете ключи, вы можете выполнить поиск в таблице, чтобы установить значение в связанной переменной.

const char *key;
const char *value;
char *query_string2 = strdup(query_string);
char *rest = query_string2;
while ((rest = parse_query_string(rest, &key, &value)) != NULL) {
    int i = key_table_find(key);
    if (i == -1) {
        /* ... unknown key ... */
        continue;
    }
    key_table[i].value = strtol(value, 0, 10);
}
free(query_string2);

Тогда интересующее вас значение может быть проиндексировано перечислением.

if (key_table[Key1] != INT_MIN) {
    /* ... do something with Key1 ... */
}

Анализ строки параметра может быть выполнен с помощью вызова strchr и strchrnul. (Если в вашей системе отсутствует strchrnul, это похоже на strchr, за исключением того, что искомый символ не найден, он возвращает указатель на '\0' в конце строки вместо NULL.)

char * parse_query_string(char *rest, const char **key, const char **value) {
    char *p, *q;
    if ((p = rest) == NULL || (q = strchr(p, '=')) == NULL) return NULL;
    *q++ = '\0';
    if (*(rest = strchrnul(q, '&'))) *rest++ = '\0';
    *key = p;
    *value = q;
    return rest;
}

Обратите внимание, что значения перечисления могут быть инициализированы соответствующими значениями хеш-функции для ускорения операции поиска.

enum {
    Key1 = KEY_TABLE_HASH('K', 'e', 'y', '1'),
    Key2 = KEY_TABLE_HASH('K', 'e', 'y', '2'),
    Key3 = KEY_TABLE_HASH('K', 'e', 'y', '3'),
    /* ... */
};

Ниже приведена гипотетическая реализация хэша. Он может обрабатывать только строки длиной до 5 байт, но он должен быть относительно простым, чтобы расширять его до более длинных строк (до максимального уровня вложенности макросов, поддерживаемого в вашей системе).

#define HASH(...)         HASH_(UP_TO_5(__VA_ARGS__), __VA_ARGS__)
#define HASH_(...)        HASH__(__VA_ARGS__)
#define HASH__(N, ...)    HASH_##N(2166136261U, __VA_ARGS__)

#define HASH_5(H, A, ...) HASH_4(HASH_1(H, A), __VA_ARGS__)
#define HASH_4(H, A, ...) HASH_3(HASH_1(H, A), __VA_ARGS__)
#define HASH_3(H, A, ...) HASH_2(HASH_1(H, A), __VA_ARGS__)
#define HASH_2(H, A, ...) HASH_1(HASH_1(H, A), __VA_ARGS__)
#define HASH_1(H, A)      (((H) ^ (unsigned)(A)) * 16777619U)

#define UP_TO_5(...) UP_TO_5_(__VA_ARGS__, 5, 4, 3, 2, 1)
#define UP_TO_5_(_1, _2, _3, _4, _5, X, ...) X

#define KEY_TABLE_HASH(...) HASH(__VA_ARGS__) % KEY_TABLE_SZ
enum { KEY_TABLE_SZ = 11 };

И функция поиска реализована с использованием того же алгоритма хеширования.

int key_table_find(const char *key) {
    unsigned hash = 2166136261U;
    const char *p = key;
    while (*p) {
        hash = HASH_1(hash, *p);
        ++p;
    }
    hash %= KEY_TABLE_SZ;
    if (key_table[hash].key == 0) return -1;
    if (strcmp(key_table[hash].key, key) != 0) return -1;
    return hash;
}

Попробуйте онлайн!

...