我被困住了。我正在学习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;我会...建议只使用一个字符串数组作为查找表"
但我不知道如何将字符串数组实际实现为查找表?
答案 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
或任何其他搜索功能。特定的键是以非常简单的方式从字符串构建的。
解决方案基于多字符常量。
多字符常量(例如'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
仔细看看,如果您有其他问题,请告诉我。正如我在开始时所说,你可以在这个练习中学到很多东西。