在C中,如何使用字符串数组作为查找表?

时间:2018-03-11 00:15:15

标签: c hashtable

我被困住了。我正在学习C并提出这个问题:

如何使用字符串数组作为查找表?

我有一个'键列表:

"A", "A#", "B", "Bb", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"

每个都会引用一个特定的int'值' (这不是唯一的)。 例如"A" -> 9"A#" -> 10"Bb" -> 10

我找到了一个答案(store known key/value pairs in c),我认为它指的是正确的方向,当它说'#34;我会...建议只使用一个字符串数组作为查找表"

但我不知道如何将字符串数组实际实现为查找表?

4 个答案:

答案 0 :(得分:1)

以最简单的方式,您可以保留两个并行的值数组,以帮助将字符串映射到整数,如下所示:

const char *keys[13];
int keytoindex[13];

/* build table */
keys[0] = "A";
keys[1] = "A#";
/* etc. */

keytoindex[0] = 9; /* Value for 'A' */
keytoindex[1] = 10;
/* etc. */

/* later */
char *userInput = ...; /* somehow get input */
int keyvalue = -1; /* I assume -1 is not in the value array, so it can be an error condition that we didn't find that key when searching in the first array */
for( int i = 0; i < 13; i++ ) {
     if(strcmp(userInput,keys[i]) == 0 )) {
          keyvalue = keytoindex[i];
          break;
     }
}

if(keyvalue == -1) exit(1); /* send an error */
/* otherwise, keep going */

当然,可以重写此代码,以便动态分配并行数组,这样可以在运行时调整它们的大小。但这个概念是一样的。

答案 1 :(得分:1)

由于您打算将字符串用作具有整数值的键,因此最好使用struct来包含这样的一对。然后建立一个表格。最后,由于您已经小心地按键排序,因此您可以使用C库函数bsearch进行查找:

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

typedef struct pair {
  char *key;
  int value;
} PAIR;

// Key strings must be in strcmp() sorted order!
PAIR table[] = {
  {"A",  9}, {"A#", 10}, {"B",  11}, {"Bb", 10}, {"C", 11}, {"C#", 12}, {"D", 13},
  {"D#", 14}, {"E", 15}, {"F", 16}, {"F#", 17}, {"G", 18}, {"G#", 19},
};

static int compare_keys(const void *va, const void *vb) {
  const PAIR *a = va, *b = vb;
  return strcmp(a->key, b->key);
}

int get_value(char *key) {
  PAIR key_pair[1] = {{key}};
  PAIR *pair = bsearch(key_pair, table,
      sizeof table / sizeof table[0], sizeof table[0], compare_keys);
  return pair ? pair->value : -1;
}

int main(void) {
  // Partial test: verify we can look up all the valid keys.
  for (int i = 0; i < sizeof table / sizeof table[0]; ++i) {
    int value = get_value(table[i].key);
    printf("%s -> %d\n", table[i].key, value);
  }
  return 0;
}

答案 2 :(得分:0)

  

我正在学习C [...]   如何实际实现一个字符串数组作为查找表?

提出了两种解决方案。

第一个解决方案是满足OP的标准(对于C初学者来说很简单,并且索引为lookup table)。 解决方案来自域的分析:密钥的性质。 在str_key表中正确排列密钥之后,导出lookup table的相应索引的过程非常简单。 不需要密集计算。没有使用strcmp或其他搜索功能。

我真的相信这是针对所提出问题的最短,最简单和最快速的解决方案。

#include <stdio.h> 
// int index   =  { 0,  1,  2,  3,  4,  5,  6,   7,  8,   9,   10,  11,  12, 13,  14  };
int value[]    =  { 9, 10, 11, 13, 17, 19,  7,  10, 10,  12,   15,  -1,  20,  8,   0  }; 

char *str_key[] = {"A","B","C","D","E","F","G","A#","Bb","C#","D#","Eb","F#","G#",NULL};

int get_value(char *key, int v[])      // magic happens here!
{
    // From string key we build corresponding index to the `value` array
                                      // Note: index for key "A" == 0  
    int index = (int) (key[0] - 'A'); // One letter key will have index from 0 to 6

    if( key[1] !=0 )                  // two letter key have his index from 7 to 13   
    {
        index = index + 7;            // index for "A#" == 7
    }

    if( (index < 0) || (index > 13) ) // protection from bad keys
        return -1;  

    return v[index];                  // return the value
}

int main(void)
{
    for (int i = 0; str_key[i] != NULL; i++) {

        int v = get_value( str_key[i], value);

        printf("%2s -> %2d\n", str_key[i], v);
    }
    return 0;
}

测试:

 A ->  9                                                                                                                                        
 B -> 10                                                                                                                                        
 C -> 11                                                                                                                                        
 D -> 13                                                                                                                                        
 E -> 17                                                                                                                                        
 F -> 19                                                                                                                                        
 G ->  7                                                                                                                                        
A# -> 10                                                                                                                                        
Bb -> 10                                                                                                                                        
C# -> 12                                                                                                                                        
D# -> 15                                                                                                                                        
Eb -> -1                                                                                                                                        
F# -> 20                                                                                                                                        
G# ->  8 

第二个简单的解决方案:

第二种解决方案不使用查找表,但也易于实现,灵活且易于理解。

这是基于

的事实
  • 一组键很小

  • 键长度最多为2个字符。

解决方案非常快。它避免了strcmp或任何其他搜索功能。特定的键是以非常简单的方式从字符串构建的。

解决方案基于多字符常量

来自Wikipedia

  

多字符常量(例如'xy')是有效的,尽管很少   使用 - 他们让一个整数存储几个字符(例如4   ASCII字符可以容纳32位整数,8位64位整数。

6.4.4.4 Character constants: 整数字符常量是one or more multibyte characters enclosed in single-quotes的序列,如'x'

代码应在gcc-Wall编译而不发出警告,并multi-character character constant”警告-pedantic。 您可以使用-Wno-multichar停用警告。

#include <stdio.h>

char *str_key[] = {"A","B","C","D","E","F","G","A#","Bb","C#","D#","Eb","F#","G#",NULL};

int build_index (char *key)
{
 // The magic happens here:
 // We take the string eg. "A#" 
 // and we construct corresponding multi-character constant 'A#'
 //                 A                  # 
    int index;

    if( key[1] == 0 )
        index = (int) key[0]; 
    else
        index = (int) ( (key[0] << 8) | key[1] );

    return index;   
}

int get_value (int key)
{
  switch (key)
    {
    case 'A':   return 9;
    case 'A#':  return 10;
    case 'B':   return 10;
    case 'Bb':  return 10;
    case 'C':   return 11;
    case 'C#':  return 12;
    case 'D':   return 13;
    case 'D#':  return 15;
    case 'E':   return 17;        
    case 'F':   return 19;
    case 'F#':  return 20;
    case 'G':   return 7;          
    case 'G#':  return 8;      
    deafult:
      break;
    }
  return -1;
}

int main (void)
{
    for (int i = 0; str_key[i] != NULL; i++) {

        int v = get_value( build_index( str_key[i] ) );

        printf("%2s -> %2d\n", str_key[i], v);
    }
    return 0;
}

测试:

 A ->  9                                                                                                              
 B -> 10                                                                                                              
 C -> 11                                                                                                              
 D -> 13                                                                                                              
 E -> 17                                                                                                              
 F -> 19                                                                                                              
 G ->  7                                                                                                              
A# -> 10                                                                                                              
Bb -> 10                                                                                                              
C# -> 12                                                                                                              
D# -> 15                                                                                                              
Eb -> -1                                                                                          
F# -> 20                                                                                
G# ->  8                                                                    

查看解决方案,如果您有其他问题,请与我们联系。谢谢!

答案 3 :(得分:0)

您可以在此练习中学到很多知识。缺少哈希表的最佳解决方案是使用stuct来形成对,正如Gene在他的回答中所示。每当您需要协调不同类型的不相关值时,您应该考虑struct

但是,根据您的问题,目前还不清楚您是否可以使用struct作为解决方案。如果没有,那么关联不同类型的值的开始方法是简单使用两个数组,其中键/值关联由数组索引提供。

正如我对您的问题的评论中所提到的,您可以轻松地将您的密钥映射到指针数组,例如:

    char *arr[] = { "A", "A#", "B", "Bb", "C", "C#", "D", /* array of pointers */
                    "D#", "E", "F", "F#", "G", "G#" };

然后,您可以将值保存在一个单独的整数数组中,该数组具有相同数量的元素,其中键"A"的索引(例如0)对应于值数组中的关联值(例如{ {1}})。这提供了values[0] = 10;"A"的简单映射。

然后给出你的指针数组和10,然后你可以遍历你的数组,试图匹配每个字符串'key'。如果在key找到匹配项,则您的相关密钥为index

将它们放在一个简单的函数中,该函数循环遍历指针数组中的每个values[index];字符串(下面为'n'),以找到提供的s并在成功时返回索引,或key如果未找到密钥,您可以执行以下操作:

-1

注意:你可以用/* locate index in 's' associated with 'key'. * for 'n' keys in 's' return 'index' of matching 'key', * otherwise return -1 if key not found. */ int getvalue (char **s, char *key, int n) { for (int i = 0; i < n; i++) { /* loop over all keys */ int found = 1; /* flag for key found */ for (int j = 0; key[j]; j++) { /* loop over all chars in key */ if (key[j] != s[i][j]) { /* if key doesn't match s[i] */ found = 0; /* set not found, break */ break; } } if (found) /* if all chars in key match s[i] */ return i; /* return index of matching key */ } return -1; } 替换内部循环,但不知道你是否有strcmp中的函数可用,只需简单地循环中的字符sting.h每次都有效。但是,如果您确实有key可用,则string.h会比滚动自己更好。您甚至可以使用strcmp来限制比较在一个大缓冲区中只是感兴趣的字符)

将所有部分组合在一个简短的示例中,该示例简单地将值随机分配给strncmp (key, s[i], KEYSIZE)数量范围内的每个键,并获取输入,让用户输入密钥以获取相关值,直到输入0 -> No. keys - 1或用户使用手动生成的"quit"取消输入(例如Linux上的 Ctrl + d 或windoze上的 Ctrl + z - 如果启用传统模式),您可以执行以下操作:

EOF

注意:提供了合理的错误处理。无论您使用哪种方式来接受用户输入(尽管我们鼓励您使用#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> enum { KEYSZ = 2, BUFSZ = 256 }; /* if you need a constant, define it */ /* simply print of all mappings */ void prnmapping (char **s, int *a, int n) { for (int i = 0; i < n; i++) printf (" %-2s : %2d\n", s[i], a[i]); } /* locate index in 's' associated with 'key'. * for 'n' keys in 's' return 'index' of matching 'key', * otherwise return -1 if key not found. */ int getvalue (char **s, char *key, int n) { for (int i = 0; i < n; i++) { /* loop over all keys */ int found = 1; /* flag for key found */ for (int j = 0; key[j]; j++) { /* loop over all chars in key */ if (key[j] != s[i][j]) { /* if key doesn't match s[i] */ found = 0; /* set not found, break */ break; } } if (found) /* if all chars in key match s[i] */ return i; /* return index of matching key */ } return -1; } int main (void) { char *arr[] = { "A", "A#", "B", "Bb", "C", "C#", "D", /*array of pointers*/ "D#", "E", "F", "F#", "G", "G#" }; int nelem = sizeof arr / sizeof *arr, /* number of elements */ values[nelem]; /* VLA for values */ srand (time (NULL)); /* initialize random seed */ for (int i = 0; i < nelem; i++) /* initialize values array */ values[i] = rand() % nelem; printf ("initial string mappings:\n"); /* just dump initial mappings */ prnmapping (arr, values, nelem); putchar ('\n'); for (;;) { /* loop continually prompting for key input until 'quit' */ char buf[BUFSZ] = "", /* buffer for line */ key[KEYSZ + 1] = ""; /* buffer for key (can just use buf) */ int index = 0; /* int to hold index matching key */ size_t len = 0; /* length of input string */ printf ("enter key ('quit' to exit): "); /* prompt */ if (fgets (buf, BUFSZ, stdin) == NULL) { /* catch manual EOF */ printf ("user canceled input.\n"); break; } if ((len = strlen (buf)) <= 1) { /* continue if empty line */ fprintf (stderr, "error: insufficient input.\n"); continue; } if (buf[len - 1] == '\n') /* check buf for trailing '\n' */ buf[--len] = 0; /* overwrite with nul-terminating char */ else { /* otherwise input equals or exceeds buffer length */ fprintf (stderr, "error: input exceeds %d chars.\n", BUFSZ - 2); break; } if (strcmp (buf, "quit") == 0) /* compare for 'quit' */ break; strncpy (key, buf, KEYSZ); /* copy KEYSZ chars from buf to key */ key[len] = 0; /* nul-terminate key (already done by initialization) */ if ((index = getvalue (arr, key, nelem)) == -1) /* key not found */ fprintf (stderr, "error: key not found.\n"); else /* success - key found, output associated value */ printf (" key: '%s' - value: %d\n", key, values[index]); } return 0; } 或POSIX fgets由于getline中存在许多缺陷,scanf,您必须验证所有输入。这意味着检查您使用的任何输入函数的返回 - 至少,并通过生成{来处理用户取消输入{1}})

示例使用/输出

scanf

仔细看看,如果您有其他问题,请告诉我。正如我在开始时所说,你可以在这个练习中学到很多东西。