在C中匹配(几个)字符串的最有效方法?

时间:2012-09-04 16:55:38

标签: c string embedded match

我们的系统需要接受来自终端的用户输入,并匹配一些已知的关键字字符串(可能是10个)。

我们没有空间/ computrons来做regexp等,代码需要很小而且快。

现在,令人讨厌的方法是:

   // str is null-terminated, assume we know it's safe/sane here
   if(!strncmp(str,"hello",5)
   {
      do_hello();
   }
   else if(!strncmp(str,"world",5)
   {
      do_world();
   }
   else
   {
      meh(); // Wasn't a match
   }

所以,经过一些谷歌搜索&阅读我确信更好的方法是将各种匹配的哈希值预先计算为int,然后只使用case语句:

// Assume hash() stops at NULL
switch(hash(str))
{
   case HASH_OF_HELLO:
      do_hello();
      break;

   case HASH_OF_WORLD:
      do_world();
      break;

   default:
      meh();
      break;
}

我们可以在编译时计算* HASH_OF_match *。这似乎是一种从相对较小的集合中选择字符串的更快/更优雅的方式。

那么 - 这看起来合情合理吗?这样做有明显的问题吗? /任何人都有更优雅的方式吗?

作为一个脚注,这是我今天下午看到的最好看的哈希算法;),归功于丹·伯恩斯坦,它看起来很适合手头的工作。

unsigned int
get_hash(const char* s)
{
    unsigned int hash = 0;
    int c;

    while((c = *s++))
    {
        // hash = hash * 33 ^ c 
        hash = ((hash << 5) + hash) ^ c;
    }

    return hash;
}

5 个答案:

答案 0 :(得分:5)

散列问题是用户输入的任意字符串可能会生成与匹配之一相同的散列,并且您将执行错误的操作。对于小至10的搜索集,我只是坚持if-else方法。或者使用字符串数组和函数指针数组(假设所有函数具有相同的签名)来选择要执行的函数。

char const *matches[10] = {"first", "second", ..., "tenth"};
void (*fn[10])(void) = {&do_first, &do_second, ..., &do_tenth};

for( i = 0; i < 10; ++i ) {
  if( strcmp( str, matches[i] ) == 0 ) {
    (*fn[i])();
  }
}

答案 1 :(得分:3)

如何在Boyer-Moore字符串搜索算法中使用最后一个字符的嵌套switch语句

http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm

答案 2 :(得分:1)

听起来您想使用gperf

答案 3 :(得分:1)

哈希表和哈希表最适合大量数据。由于输入字符串的数量是已知且有限的,您可以考虑这种方法:

让我们假设已知的字符串是

const char* STR_TABLE [STR_N] =
{
  "hello",
  "world",
  "this",
  "is",
  "a",
  "number",
  "of",
  "ten",
  "test",
  "strings"
};

然后我们可以在编译之前按字母顺序手动对它们进行排序,因为排序表提供了更快的搜索可能性。然后,您可以使用二进制搜索。

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

#define STR_N 10


const char* STR_TABLE [STR_N] =
{
  "a",
  "hello",
  "is",
  "number",
  "of",
  "strings",
  "ten",
  "test",
  "this",
  "world"
};


int ptr_strcmp (const void* str1, const void* str2)
{
  return strcmp(str1, *(const char**)str2);
}

int main()
{
  const char* user_input = "world"; // worst case
  const char** result;

  result = bsearch (user_input,
                    STR_TABLE,
                    STR_N,
                    sizeof(const char*),
                    ptr_strcmp);

  if(result != NULL)
  {
    printf("%s\n", *result);
  }
  else
  {
    printf("meh\n");
  }

}

这将归结为:

  

将“world”与“of”进行比较,1比较'w'!='o'。

     

将“world”与“test”进行比较,1比较'w'!='t'。

     

将“world”与“this”进行比较,1比较'w'!='t'。

     

将“世界”与“世界”进行比较,进行5次比较。

     

比较总数为8。

当然有一些开销代码,检查'\ 0'和二进制搜索调用。您必须在特定平台上测量建议的各种方法,以找出最佳方法。

答案 4 :(得分:0)

也许解决方案可能是这样的:

struct keyword {
    unsigned int hash;
    const char *str;
    void (*job)();
};

//A table with our keywords with their corresponding hashes. If you could not
//compute the hash at compile time, a simple init() function at the beginning
//of your program could initialize each entry by using the value in 'str'
//You could also implement a dynamic version of this table (linked list of keywords)
//for extending your keyword table during runtime
struct keyword mykeywords[] = {
    {.hash = HASH_OF_HELLO, .str = "hello", .job = do_hello},
    {.hash = HASH_OF_WORLD, .str = "world", .job = do_world},
    ...
    {.str = 0} //signal end of list of keywords

};

void run(const char *cmd)
{
    unsigned int cmdhash = get_hash(cmd);
    struct keyword *kw = mykeywords;
    while(kw->str) {
        //If hash matches then compare the string, since we should consider hashing collisions too!
        //The order of conditions below is important
        if (kw->hash == cmdhash && !strcmp(cmd, kw->str)) { 
             kw->job();
             break;   
        }
        kw++;
    }
}