Лучший способ включить строку в C
В C есть конструкция switch
, которая позволяет выполнять различные условные ветки кода на основе целочисленного значения теста, например,
int a;
/* Read the value of "a" from some source, e.g. user input */
switch ( a ) {
case 100:
// Code
break;
case 200:
// Code
break;
default:
// Code
break;
}
Как можно получить такое же поведение (т.е. Избежать так называемой " if
- else
лестницы") для строкового значения, то есть char *
?
Ответы
Ответ 1
Если вы имеете в виду, как написать что-то похожее на это:
// switch statement
switch (string) {
case "B1":
// do something
break;
/* more case "xxx" parts */
}
Тогда каноническое решение в C состоит в использовании лестницы if-else:
if (strcmp(string, "B1") == 0)
{
// do something
}
else if (strcmp(string, "xxx") == 0)
{
// do something else
}
/* more else if clauses */
else /* default: */
{
}
Ответ 2
Если у вас много случаев и вы не хотите писать тонну вызовов strcmp()
, вы можете сделать что-то вроде:
switch(my_hash_function(the_string)) {
case HASH_B1: ...
/* ...etc... */
}
Вам просто нужно убедиться, что ваша хэш-функция не имеет коллизий внутри набора возможных значений для строки.
Ответ 3
Нет никакого способа сделать это в C. Есть много разных подходов. Как правило, самое простое - определить набор констант, представляющих ваши строки, и выполнить поиск по строке, чтобы получить константу:
#define BADKEY -1
#define A1 1
#define A2 2
#define B1 3
#define B2 4
typedef struct { char *key; int val; } t_symstruct;
static t_symstruct lookuptable[] = {
{ "A1", A1 }, { "A2", A2 }, { "B1", B1 }, { "B2", B2 }
};
#define NKEYS (sizeof(lookuptable)/sizeof(t_symstruct))
int keyfromstring(char *key)
{
int i;
for (i=0; i < NKEYS; i++) {
t_symstruct *sym = lookuptable[i];
if (strcmp(sym->key, key) == 0)
return sym->val;
}
return BADKEY;
}
/* ... */
switch (keyfromstring(somestring)) {
case A1: /* ... */ break;
case A2: /* ... */ break;
case B1: /* ... */ break;
case B2: /* ... */ break;
case BADKEY: /* handle failed lookup */
}
Есть, конечно, более эффективные способы сделать это. Если вы сохраняете свои ключи отсортированными, вы можете использовать бинарный поиск. Вы также можете использовать хеш-таблицу. Эти вещи меняют вашу производительность за счет обслуживания.
Ответ 4
Я думаю, что лучший способ сделать это - отделить "признание" от функциональности:
struct stringcase { char* string; void (*func)(void); };
void funcB1();
void funcAzA();
stringcase cases [] =
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};
void myswitch( char* token ) {
for( stringcases* pCase = cases
; pCase != cases + sizeof( cases ) / sizeof( cases[0] )
; pCase++ )
{
if( 0 == strcmp( pCase->string, token ) ) {
(*pCase->func)();
break;
}
}
}
Ответ 5
Мой предпочтительный метод для этого - через хеш-функцию (заимствованную из здесь). Это позволяет использовать эффективность оператора switch даже при работе с char *:
#include "stdio.h"
#define LS 5863588
#define CD 5863276
#define MKDIR 210720772860
#define PWD 193502992
const unsigned long hash(const char *str) {
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c;
return hash;
}
int main(int argc, char *argv[]) {
char *p_command = argv[1];
switch(hash(p_command)) {
case LS:
printf("Running ls...\n");
break;
case CD:
printf("Running cd...\n");
break;
case MKDIR:
printf("Running mkdir...\n");
break;
case PWD:
printf("Running pwd...\n");
break;
default:
printf("[ERROR] '%s' is not a valid command.\n", p_command);
}
}
Конечно, этот подход требует, чтобы хэш-значения для всех возможных принятых char * вычислялись заранее. Я не думаю, что это слишком большая проблема; однако, поскольку оператор switch работает независимо от фиксированных значений. Простую программу можно выполнить, чтобы передать char * через хэш-функцию и вывести их результаты. Эти результаты затем могут быть определены с помощью макросов, как я сделал выше.
Ответ 6
Чтобы добавить к Phimueme ответ выше, если ваша строка всегда состоит из двух символов, вы можете построить 16-битный int из двух 8-битных символов - и включить его (чтобы избежать вложенных операторов switch/case).
Ответ 7
Есть способ быстрее выполнить строковый поиск. Предположения: поскольку мы говорим об операторе switch, я могу предположить, что значения не будут меняться во время выполнения.
Идея состоит в использовании C stdlib qsort и bsearch.
Я буду работать над кодом xtofl.
struct stringcase { char* string; void (*func)(void); };
void funcB1();
void funcAzA();
struct stringcase cases [] =
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};
struct stringcase work_cases* = NULL;
int work_cases_cnt = 0;
// prepare the data for searching
void prepare() {
// allocate the work_cases and copy cases values from it to work_cases
qsort( cases, i, sizeof( struct stringcase ), stringcase_cmp );
}
// comparator function
int stringcase_cmp( const void *p1, const void *p2 )
{
return strcasecmp( ((struct stringcase*)p1)->string, ((struct stringcase*)p2)->string);
}
// perform the switching
void myswitch( char* token ) {
struct stringcase val;
val.string=token;
void* strptr = bsearch( &val, work_cases, work_cases_cnt, sizeof( struct stringcase), stringcase_cmp );
if (strptr) {
struct stringcase* foundVal = (struct stringcase*)strptr;
(*foundVal->func)();
return OK;
}
return NOT_FOUND;
}
Ответ 8
Я опубликовал заголовочный файл для выполнения переключения строк в C. Он содержит набор макросов, которые скрывают вызов strcmp() (или аналогичный), чтобы имитировать поведение, подобное переключению.
Я тестировал его только с GCC в Linux, но я уверен, что он может быть адаптирован для поддержки другой среды.
EDIT: добавлен код здесь, в соответствии с запросом
Это заголовочный файл, который вы должны включить:
#ifndef __SWITCHS_H__
#define __SWITCHS_H__
#include <string.h>
#include <regex.h>
#include <stdbool.h>
/** Begin a switch for the string x */
#define switchs(x) \
{ char *__sw = (x); bool __done = false; bool __cont = false; \
regex_t __regex; regcomp(&__regex, ".*", 0); do {
/** Check if the string matches the cases argument (case sensitive) */
#define cases(x) } if ( __cont || !strcmp ( __sw, x ) ) \
{ __done = true; __cont = true;
/** Check if the string matches the icases argument (case insensitive) */
#define icases(x) } if ( __cont || !strcasecmp ( __sw, x ) ) { \
__done = true; __cont = true;
/** Check if the string matches the specified regular expression using regcomp(3) */
#define cases_re(x,flags) } regfree ( &__regex ); if ( __cont || ( \
0 == regcomp ( &__regex, x, flags ) && \
0 == regexec ( &__regex, __sw, 0, NULL, 0 ) ) ) { \
__done = true; __cont = true;
/** Default behaviour */
#define defaults } if ( !__done || __cont ) {
/** Close the switchs */
#define switchs_end } while ( 0 ); regfree(&__regex); }
#endif // __SWITCHS_H__
И вот как вы его используете:
switchs(argv[1]) {
cases("foo")
cases("bar")
printf("foo or bar (case sensitive)\n");
break;
icases("pi")
printf("pi or Pi or pI or PI (case insensitive)\n");
break;
cases_re("^D.*",0)
printf("Something that start with D (case sensitive)\n");
break;
cases_re("^E.*",REG_ICASE)
printf("Something that start with E (case insensitive)\n");
break;
cases("1")
printf("1\n");
cases("2")
printf("2\n");
break;
defaults
printf("No match\n");
break;
} switchs_end;
Ответ 9
Как правило, я это делаю.
void order_plane(const char *p)
{
switch ((*p) * 256 + *(p+1))
{
case 0x4231 : /* B1 */
{
printf("Yes, order this bomber. It a blast.\n");
break;
}
case 0x5354 : /* ST */
{
printf("Nah. I just can't see this one.\n");
break;
}
default :
{
printf("Not today. Can I interest you in a crate of SAMs?\n";
}
}
}
Ответ 10
Вот как вы это делаете. Нет, не совсем.
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#define p_ntohl(u) ({const uint32_t Q=0xFF000000; \
uint32_t S=(uint32_t)(u); \
(*(uint8_t*)&Q)?S: \
( (S<<24)| \
((S<<8)&0x00FF0000)| \
((S>>8)&0x0000FF00)| \
((S>>24)&0xFF) ); })
main (void)
{
uint32_t s[0x40];
assert((unsigned char)1 == (unsigned char)(257));
memset(s, 0, sizeof(s));
fgets((char*)s, sizeof(s), stdin);
switch (p_ntohl(s[0])) {
case 'open':
case 'read':
case 'seek':
puts("ok");
break;
case 'rm\n\0':
puts("not authorized");
break;
default:
puts("unrecognized command");
}
return 0;
}
Ответ 11
Если это 2-байтовая строка, вы можете сделать что-то вроде этого конкретного примера, когда я включаю коды языка ISO639-2.
LANIDX_TYPE LanCodeToIdx(const char* Lan)
{
if(Lan)
switch(Lan[0]) {
case 'A': switch(Lan[1]) {
case 'N': return LANIDX_AN;
case 'R': return LANIDX_AR;
}
break;
case 'B': switch(Lan[1]) {
case 'E': return LANIDX_BE;
case 'G': return LANIDX_BG;
case 'N': return LANIDX_BN;
case 'R': return LANIDX_BR;
case 'S': return LANIDX_BS;
}
break;
case 'C': switch(Lan[1]) {
case 'A': return LANIDX_CA;
case 'C': return LANIDX_CO;
case 'S': return LANIDX_CS;
case 'Y': return LANIDX_CY;
}
break;
case 'D': switch(Lan[1]) {
case 'A': return LANIDX_DA;
case 'E': return LANIDX_DE;
}
break;
case 'E': switch(Lan[1]) {
case 'L': return LANIDX_EL;
case 'N': return LANIDX_EN;
case 'O': return LANIDX_EO;
case 'S': return LANIDX_ES;
case 'T': return LANIDX_ET;
case 'U': return LANIDX_EU;
}
break;
case 'F': switch(Lan[1]) {
case 'A': return LANIDX_FA;
case 'I': return LANIDX_FI;
case 'O': return LANIDX_FO;
case 'R': return LANIDX_FR;
case 'Y': return LANIDX_FY;
}
break;
case 'G': switch(Lan[1]) {
case 'A': return LANIDX_GA;
case 'D': return LANIDX_GD;
case 'L': return LANIDX_GL;
case 'V': return LANIDX_GV;
}
break;
case 'H': switch(Lan[1]) {
case 'E': return LANIDX_HE;
case 'I': return LANIDX_HI;
case 'R': return LANIDX_HR;
case 'U': return LANIDX_HU;
}
break;
case 'I': switch(Lan[1]) {
case 'S': return LANIDX_IS;
case 'T': return LANIDX_IT;
}
break;
case 'J': switch(Lan[1]) {
case 'A': return LANIDX_JA;
}
break;
case 'K': switch(Lan[1]) {
case 'O': return LANIDX_KO;
}
break;
case 'L': switch(Lan[1]) {
case 'A': return LANIDX_LA;
case 'B': return LANIDX_LB;
case 'I': return LANIDX_LI;
case 'T': return LANIDX_LT;
case 'V': return LANIDX_LV;
}
break;
case 'M': switch(Lan[1]) {
case 'K': return LANIDX_MK;
case 'T': return LANIDX_MT;
}
break;
case 'N': switch(Lan[1]) {
case 'L': return LANIDX_NL;
case 'O': return LANIDX_NO;
}
break;
case 'O': switch(Lan[1]) {
case 'C': return LANIDX_OC;
}
break;
case 'P': switch(Lan[1]) {
case 'L': return LANIDX_PL;
case 'T': return LANIDX_PT;
}
break;
case 'R': switch(Lan[1]) {
case 'M': return LANIDX_RM;
case 'O': return LANIDX_RO;
case 'U': return LANIDX_RU;
}
break;
case 'S': switch(Lan[1]) {
case 'C': return LANIDX_SC;
case 'K': return LANIDX_SK;
case 'L': return LANIDX_SL;
case 'Q': return LANIDX_SQ;
case 'R': return LANIDX_SR;
case 'V': return LANIDX_SV;
case 'W': return LANIDX_SW;
}
break;
case 'T': switch(Lan[1]) {
case 'R': return LANIDX_TR;
}
break;
case 'U': switch(Lan[1]) {
case 'K': return LANIDX_UK;
case 'N': return LANIDX_UN;
}
break;
case 'W': switch(Lan[1]) {
case 'A': return LANIDX_WA;
}
break;
case 'Z': switch(Lan[1]) {
case 'H': return LANIDX_ZH;
}
break;
}
return LANIDX_UNDEFINED;
}
LANIDX_ * - постоянные целые числа, используемые для индексирования в массивах.
Ответ 12
Мы не можем уйти от лестницы if-else, чтобы сравнить строку с другими. Даже обычный switch-case также является внутренним лестницей if-else (для целых чисел). Мы могли бы только симулировать случай переключения для строки, но никогда не могли бы заменить лестницу if-else. Лучшие из алгоритмов сравнения строк не могут избежать использования функции strcmp. Означает сравнивать символ за символом, пока не будет найдено совпадение. Поэтому использование if-else ladder и strcmp неизбежно.
DEMO
А вот простейшие макросы для имитации переключателя для строк.
#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
#define SWITCH(X) for (char* __switch_p__ = X, int __switch_next__=1 ; __switch_p__ ; __switch_p__=0, __switch_next__=1) { {
#define CASE(X) } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
#define DEFAULT } {
#define END }}
#endif
И вы можете использовать их как
char* str = "def";
SWITCH (str)
CASE ("abc")
printf ("in abc\n");
break;
CASE ("def") // Notice: 'break;' statement missing so the control rolls through subsequent CASE until DEFAULT
printf("in def\n");
CASE ("ghi")
printf ("in ghi\n");
DEFAULT
printf("in DEFAULT\n");
END
Выход:
in def
in ghi
in DEFAULT
Ниже показано использование вложенного SWITCH:
char* str = "def";
char* str1 = "xyz";
SWITCH (str)
CASE ("abc")
printf ("in abc\n");
break;
CASE ("def")
printf("in def\n");
SWITCH (str1) // <== Notice: Nested SWITCH
CASE ("uvw")
printf("in def => uvw\n");
break;
CASE ("xyz")
printf("in def => xyz\n");
break;
DEFAULT
printf("in def => DEFAULT\n");
END
CASE ("ghi")
printf ("in ghi\n");
DEFAULT
printf("in DEFAULT\n");
END
Выход:
in def
in def => xyz
in ghi
in DEFAULT
Вот обратная строка SWITCH, в которой вы можете использовать переменную (а не константу) в предложении CASE:
char* str2 = "def";
char* str3 = "ghi";
SWITCH ("ghi") // <== Notice: Use of variables and reverse string SWITCH.
CASE (str1)
printf ("in str1\n");
break;
CASE (str2)
printf ("in str2\n");
break;
CASE (str3)
printf ("in str3\n");
break;
DEFAULT
printf("in DEFAULT\n");
END
Выход:
in str3
Ответ 13
Предполагая, что маленькая endianness и sizeof (char) == 1, вы могли бы сделать это (что-то вроде этого было предложено Майком Бромом).
char* txt = "B1";
int tst = *(int*)txt;
if ((tst & 0x00FFFFFF) == '1B')
printf("B1!\n");
Его можно было бы обобщить для случая BE.
Ответ 14
Хорошо, это сработало со мной.
Подходом является то, что вы берете первую букву и берете оставшуюся букву в корпусе коммутатора.
#include<stdio.h>
#include<string.h>
int main()
{
char str[10];//this is the string we are taking
scanf("%s",str);
switch(str[0])
case str[1]:
if(str[2] && str[3])
{
printf("Your condition");
}
else
{
printf("not expected string");
}
}
Хорошо, если у вас есть строка больше 3 или более символов, рассмотрите возможность использования strstr в условии if
Например:
switch(str[0])
case str[1]:
if(strstr(str,"req"))//similar to string comparision
{
printf("required condition");
}
Ответ 15
Функциональные указатели являются отличным способом сделать это, например,
result = switchFunction(someStringKey);//result is an optional return value
... это вызывает функцию, которую вы установили с помощью строкового ключа (одна функция на случай):
setSwitchFunction("foo", fooFunc);
setSwitchFunction("bar", barFunc);
Используйте уже существующую реализацию hashmap/table/dictionary, такую как khash, верните этот указатель на функцию внутри switchFunction()
и выполните ее (или просто верните ее из switchFunction()
и выполните ее самостоятельно). Если реализация карты не хранит это, просто используйте вместо этого uint64_t
который вы приводите в соответствии с указателем.
Ответ 16
Привет, это простой и быстрый способ, если у вас есть этот случай:
[БЫСТРЫЙ режим]
int concated;
char ABC[4]="";int a=1,b=4,c=2; //char[] Initializing
ABC<-sprintf(ABC,"%d%d%d",a,b,c); //without space between %d%d%d
printf("%s",ABC); //value as char[] is =142
concated=atoi(ABC); //result is 142 as int, not 1,4,2 (separeted)
//now use switch case on 142 as an integer and all possible cases
[РАЗРАБОТАННЫЙ режим]
например:
у меня есть много меню, каждый выбор в 1-м меню принимает u во второе меню,
то же самое со 2-м меню и 3-м меню. но опции различаются
поэтому вы знаете, что пользователь выбрал финналию.
Пример:
меню 1:1 == > меню 2: 4 == > меню 3: 2 (...) выбор 142. другие случаи: 111,141,131,122...
гуманный:
храните первый 1-й в a, 2-й в b, 3-й по c. a = 1, b = 4, c = 2
char ABC[4]="";
ABC<-sprintf(ABC,"%d%d%d",a,b,c); //without space between %d%d%d
printf("%s",ABC); //value as char[]=142
//now you want to recover your value(142) from char[] to int as int value 142
concated=atoi(ABC); //result is 142 as int, not 1,4,2 (separeted)