C: 将字符串映射到结构字段上

问题描述 投票:0回答:1

在解析HTTP响应时,我需要将响应头值分配给结构中匹配的头字段。比如说

struct http_response {
char* content_type;
char* date;
char* server;
char* via;
char* connection;
char* expires;
};

HTTP响应。

HTTP/1.1 200 OK
Content-Type: text/html; charset=ISO-8859-1
Date: Wed, 10 Jun 2020 17:30:46 GMT
Server: gws
Expires: Wed, 10 Jun 2020 17:30:46 GMT
Cache-Control: private
Via: 1.1 localhost (squid/4.11)
Connection: close

映射

http_response->content_type = "text/html; charset=ISO-8859-1"
http_response->date = "Wed, 10 Jun 2020 17:30:46 GMT"
/* ... */

天真的做法是 strcasecmp()'ing并将值分配给结构字段;这对于小尺寸的结构似乎是可以的。但我有35 - 40个字段,而写40个 if-else 块似乎是最后的手段。利用结构的内存布局,以及同质场(char*),我可以调整一下,以消除 if-else 块?

c pointers struct
1个回答
2
投票

我认为有两种可能的选择。

使用数据驱动的方法

创建一个表,将头字段名称映射到 补偿 变成 struct http_response,比如像这样。

#include <stddef.h>

struct table_entry {
    const char *name;
    size_t offset;
};

static const table_entry table[] = {
    {"Content-Type", offsetof(struct table_entry, content_type)},
    {"Date",         offsetof(struct table_entry, date)},
    ...
};

然后在解析每个头行的时候, 搜索那个表找到匹配的字段名, 然后你就知道在结构中的哪个位置放置值. 如果你确保表的排序方式是 key那么你可以使用 bsearch() 来找到一个对数时间的键。

static int compare_entries(const void *a, const void *b) {
    const struct table_entry *entry_a = a;
    const struct table_entry *entry_b = b;
    return strcasecmp(entry_a->name, entry_b->name);
}

...

struct http_response response = {NULL};

// Parse a line from the header
char *field = /* header field name */;
char *value = /* value */;

// Search for the key in the table
struct table_entry search_entry = {field};
struct table_entry *match = bsearch(&search_entry, table, sizeof(table) / sizeof(*table), sizeof(*table), compare_entries);

// If it's found, update response
if (match) {
    char **value_ptr = (char **)((char *)&response + match->offset);
    *value_ptr = value;
}

不要建立一个包含所有可能的头字段的结构。

有很多可能的HTTP头字段,但一个典型的头块将只包含其中的几个。不建立 struct header_field但只需制作一个函数来查询当前头块中所需的字段。如果你想对一个单一的头块进行大量的查询,那么假设你已经把它分割成了单独的行,你可能会想在行中使用 qsort() 首先,您可以使用 bsearch() 再次。


1
投票

你可以使用你感兴趣的头信息表和结构中相应指针的字节偏移量。然后测试HTTP请求中是否有匹配的头文件。如果找到了就把它分配到结构体中相应指针所在的相应偏移处。

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

typedef struct http_response {
char* content_type;
char* date;
char* server;
char* via;
char* connection;
char* expires;
} http_response;

// a type to hold header name and a byte offset in the structure
typedef struct keyval
{
    const char *key;
    size_t offset;
} keyval;

// The headers we are interest in and their byte offsets 
// from the beginning of the structure
static const keyval kv[]  = {
    {"Content-Type", offsetof(http_response, content_type)},
    {"Date", offsetof(http_response, date)},
    {"Server", offsetof(http_response, server)},
    {"Via", offsetof(http_response, via)},
    {"Connection", offsetof(http_response, connection)},
    {"Expires", offsetof(http_response, expires)},

    {NULL, 0}
};

// check each headere line and assign it to the corresponding
// structure field, if it is a header we are interested in
bool assign_header(http_response *resp, const char *hdr_line)
{
    bool found = false;
    char *p = strchr(hdr_line, ':');
    char *pr = (char *)resp;
    if (p)
    {
        int i;
        *p = 0;
        for (i=0; kv[i].key != NULL; i++)
            if (strcmp(kv[i].key, hdr_line) == 0)
            {
                *(char **)(pr + kv[i].offset) = p + 1;
                found = true;
                break;
            }
    }
    return found;
}

// test code
int main()
{
    int i;
    http_response resp;
    char test[] = 
"Content-Type: text/html; charset=ISO-8859-1\n\
Date: Wed, 10 Jun 2020 17:30:46 GMT\n\
Server: gws\n\
Expires: Wed, 10 Jun 2020 17:30:46 GMT\n\
Cache-Control: private\n\
Via: 1.1 localhost (squid/4.11)\n\
Connection: close\n\
";
    memset(&resp, 0, sizeof(resp));
    char *rest = test;
    char *p =strtok_r(test, "\n", &rest);
    while (p)
    {
        assign_header(&resp, p);
        p =strtok_r(NULL, "\n", &rest);
    }

    // test a few fields
    printf("Content-Type => %s\n", resp.content_type);
    printf("Server => %s\n", resp.server);
    printf("Expires => %s\n", resp.expires);

    return 0;
}

发完帖子后,我看到也有人发了一个类似逻辑的答案:)

上面的代码将指针分配给结构体。你不妨修改一下,改成分配值的分配副本,以后再写一个函数来释放所有分配的内存。

© www.soinside.com 2019 - 2024. All rights reserved.