簡體   English   中英

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

[英]Most efficient way to match (a few) strings in C?

我們的系統需要接受來自終端的用戶輸入,並與一些已知的關鍵字字符串(可能為10)匹配。

我們沒有空間/計算機來進行正則表達式等,因此代碼必須小巧而快速。

現在,執行此操作的討厭方法是:

   // 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 *。 這似乎是從相對較小的集合中選擇字符串的一種更快/更優雅的方法。

所以-這看起來合理嗎? /這樣做有明顯的問題嗎? /有人有更優雅的方法嗎?

作為一個腳注,這是我今天下午見過的最漂亮的哈希算法;),歸功於dan bernstein,它看起來很適合手頭的工作。

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;
}

散列的問題在於,用戶輸入的任意字符串可能會產生與您的匹配項相同的散列,並且您將執行錯誤的操作。 對於只有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])();
  }
}

像在Boyer-Moore字符串搜索算法中一樣,僅對最后一個字符使用嵌套的switch語句該怎么辦?

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

聽起來像您想使用gperf

哈希表和哈希表最適合大量數據。 由於輸入字符串的數目是已知的並且是有限的,因此您可以考慮采用以下方法:

假設已知字符串為

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”。

比較“世界”與“測試”,1比較“ w”!=“ t”。

比較“ world”和“ this”,比較1個“ w”!=“ t”。

比較“世界”與“世界”,進行5個比較。

比較總數為8。

當然,其中涉及一些開銷代碼,以檢查'\\ 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++;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM