hash表--c语言 字符串键值配对——(key, value)

c语言键值配对——(key, value)

看一个C++项目时,其中解析配置文的部分引发了我的思考。
配置文件问普通字符文件,内容都是类似 如下:
ipaddr=127.0.0.1
port=888
logfile=log
C++对此配置文件解析字符,按每次处理一行,以”=”作为分隔符将每行分成两个字符串作为(key,value)插入map 变量,举个例子
以ipaddr=127.0.0.1为例:

std::map<std::string, std::string> config_map;
//key,value = “ipaddr","127.0.0.1"
config_map..insert(std::make_pair("ipaddr", "127.0.0.1");

这个提取配置信息作为键值对 在C++中很容易实现,但是在C项目中又该如何实现呢,好像没有直接以字符串作为索引的数组,实现方式需要特殊处理。
我想到的是哈希表,这个东西类似于数组,但是索引值是以 字符串计算而来,关于哈希值得计算公式其实很简单,就是对字符串进行一系列操作。
网上有个网友写了一篇很不错的关于哈希表的文章:
https://blog.csdn.net/txl199106/article/details/40184965
其中对于哈希表的链地址法实现Hash写的非常好,链地址能在一定程度上对键冲突进行处理,如果键冲突太频繁,获取就要考虑另一种方式实现了,但是对于今天的主题,链地址hash表已经足够,并且简单。
现附上那位网友的源码,后面我会稍微修改一下,来演示键值对冲突:
代码来源
https://blog.csdn.net/txl199106/article/details/40184965

#include 
#include 
#include 

typedef struct _node{
    char *name;
    char *desc;
    struct _node *next;
}node;

#define HASHSIZE 101
static node* hashtab[HASHSIZE];
void inithashtab(){
    int i;
    for(i=0;iunsigned int hash(char *s){
    unsigned int h=0;
    for(;*s;s++)
        h=*s+h*31;
    return h%HASHSIZE;
}

node* lookup(char *n){
    unsigned int hi=hash(n);
    node* np=hashtab[hi];
    for(;np!=NULL;np=np->next){
        if(!strcmp(np->name,n))
            return np;
    }
    return NULL;
}

char* m_strdup(char *o){
    int l=strlen(o)+1;
    char *ns=(char*)malloc(l*sizeof(char));
    strcpy(ns,o);
    if(ns==NULL)
        return NULL;
    else
        return ns;
}

char* get(char* name){
    node* n=lookup(name);
    if(n==NULL)
        return NULL;
    else
        return n->desc;
}

int install(char* name,char* desc){
    unsigned int hi;
    node* np;
    if((np=lookup(name))==NULL){
        hi=hash(name);
        np=(node*)malloc(sizeof(node));
        if(np==NULL)
            return 0;

        np->name=m_strdup(name);
        if(np->name==NULL) return 0;
        np->next=hashtab[hi];
        hashtab[hi]=np;
    }
    else
        free(np->desc);
    np->desc=m_strdup(desc);
    if(np->desc==NULL) return 0;
    return 1;
}

/* A pretty useless but good debugging function,
which simply displays the hashtable in (key.value) pairs
*/

void displaytable(){
    int i;
    node *t;
    for(i=0;iif(hashtab[i]==NULL)
            printf("()");
        else{
            t=hashtab[i];
            printf("(");
            for(;t!=NULL;t=t->next)
                printf("(%s.%s) ",t->name,t->desc);
            printf(".)\n");
        }
    }
}

void cleanup(){
    int i;
    node *np,*t;
    for(i=0;iif(hashtab[i]!=NULL){
            np=hashtab[i];
            while(np!=NULL){
                t=np->next;
                free(np->name);
                free(np->desc);
                free(np);
                np=t;
            }
        }
    }
}

main(){
    int i;
    char* names[]={"name","address","phone","k101","k110"};
    char* descs[]={"Sourav","Sinagor","26300788","Value1","Value2"};
    inithashtab();
//增加部分--------------------------打印hash_table_index   
    for(i = 0; i < 5; i++)
    {
        unsigned int hi=hash(names[i]);
        printf("%s--hash_table_index=%d\n", names[i], hi);
    }
//---------------------------------------打印hash_table_index
    for(i=0;i<5;i++)
        install(names[i],descs[i]);
    printf("Done\n");
    printf("If we didnt do anything wrong..""we should see %s\n",get("k110"));
    install("phone","9433120451");
    printf("Again if we go right, we have %s and %s\n",get("k101"),get("phone"));
    displaytable();
    cleanup();

    return 0;
}

在此代码中,我添加了打印 字符串 对应的hash值

//增加部分--------------------------打印hash_table_index   
    for(i = 0; i < 5; i++)
    {
        unsigned int hi=hash(names[i]);
        printf("%s--hash_table_index=%d\n", names[i], hi);
    }
//---------------------------------------打印hash_table_index

输出信息
hash表--c语言 字符串键值配对——(key, value)_第1张图片
可以看见,不同字符串获得hash值不一样,目前还没有键冲突,那么每个字符串在哈希表(static node* hashtab[HASHSIZE])中的索引便知道了, 打印信息很直白,结合源码很容易看懂。
下面来模拟键冲突的情况,其实要想两个不同的字符串对应的hash值相同,即键冲突,这样的两个字符串很难找到,不妨修改hash值的计算方式,给定一个固定值,修改如下代码:

unsigned int hash(char *s){
    unsigned int h=0;
    /**
    for(;*s;s++)
        h=*s+h*31;
    **/
    return h%HASHSIZE;
}

让其获得的hash值都为0,所以不管怎样都会出现hash值相同,即键冲突
输出信息如下:
hash表--c语言 字符串键值配对——(key, value)_第2张图片
所有的键值对都在hash表的第一项中,冲突键使用单链表连接起来了,具体是在插入新字符串键时先查找已有hash表中是否存在,如果键相等再比较字符串是否相等,见下:

node* lookup(char *n){
    unsigned int hi=hash(n);
    node* np=hashtab[hi];
    for(;np!=NULL;np=np->next){
        if(!strcmp(np->name,n))
            return np;
    }
    return NULL;
}

如果字符串相等,返回已有的键值对,覆盖对应的值,如果查找不存在,申请新的空间,从链表头部插入新键值对。
在C语言中链式哈希表能实现类似 C++ 中map 的键值对功能,有这个需求的可以参考以上源码内容。

你可能感兴趣的:(C,链式hash表)