如何使用 C 语言处理 http 服务器接收到的 <form> 数据,例如 (key=value&key=value&key=value&...) 并将每个值分配给一个变量?
How to handle <form> data received by http server e.g (key=value&key=value&key=value&...) using C language and assign each value to a variable?
我在微控制器上安装了 http 服务器 运行。它提供一个简短的 html 网页,其中包含一个表单。使用 POST 方法填写表格并单击提交后,我收到如下表格值:
Key1=value1&Key2=value2&Key3=value3&...
接收到的全部数据作为字符串保存在缓冲区中。
问题是:如何通过将每个 key=vale 保存在变量中来处理这些数据。例如:
int key1 = value1
int key2 = value2
int key3 = value3
非常感谢
如果您只对一组固定的键感兴趣,那么您可能需要一个 table 将键与相应的变量相关联。例如:
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 },
/* ... */
};
当您解析查询参数字符串并识别键时,您可以查找 table 以将值设置到关联变量中。
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
,除非找不到搜索字符,它 returns 指向 '[=21=]'
末尾的指针字符串而不是 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++ = '[=13=]';
if (*(rest = strchrnul(q, '&'))) *rest++ = '[=13=]';
*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;
}
这是我的热门评论。
这是一个带有测试用例的完整实现:
#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;
}
我在微控制器上安装了 http 服务器 运行。它提供一个简短的 html 网页,其中包含一个表单。使用 POST 方法填写表格并单击提交后,我收到如下表格值:
Key1=value1&Key2=value2&Key3=value3&...
接收到的全部数据作为字符串保存在缓冲区中。
问题是:如何通过将每个 key=vale 保存在变量中来处理这些数据。例如:
int key1 = value1
int key2 = value2
int key3 = value3
非常感谢
如果您只对一组固定的键感兴趣,那么您可能需要一个 table 将键与相应的变量相关联。例如:
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 },
/* ... */
};
当您解析查询参数字符串并识别键时,您可以查找 table 以将值设置到关联变量中。
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
,除非找不到搜索字符,它 returns 指向 '[=21=]'
末尾的指针字符串而不是 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++ = '[=13=]';
if (*(rest = strchrnul(q, '&'))) *rest++ = '[=13=]';
*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;
}
这是我的热门评论。
这是一个带有测试用例的完整实现:
#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;
}