[c] ‘switch’문을 사용하여 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;
}

문자열 값에 대해 동일한 동작 (즉, 소위 ” ifelse래더”를 피)을 얻을 수있는 방법은 char *무엇입니까?



답변

의미하는 경우 다음과 유사한 내용을 작성하는 방법 :

// 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: */
{
}


답변

케이스가 많고 많은 strcmp()통화 를 작성하고 싶지 않은 경우 다음과 같이 할 수 있습니다.

switch(my_hash_function(the_string)) {
    case HASH_B1: ...
    /* ...etc... */
}

해시 함수가 문자열의 가능한 값 집합 내에 충돌이 없는지 확인하기 만하면됩니다.


답변

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 */
}

물론이를 수행하는 더 효율적인 방법이 있습니다. 키를 정렬 된 상태로 유지하면 이진 검색을 사용할 수 있습니다. 해시 테이블도 사용할 수 있습니다. 이러한 것들은 유지 보수 비용으로 성능을 변경합니다.


답변

이 작업을 수행하는 데 선호하는 방법은 해시 함수를 사용하는 것입니다 ( 여기 에서 빌림 ). 이렇게하면 char *로 작업 할 때에도 switch 문의 효율성을 활용할 수 있습니다.

#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 *를 전달하고 그 결과를 출력하는 간단한 프로그램을 만들 수 있습니다. 이러한 결과는 위에서 한 것처럼 매크로를 통해 정의 할 수 있습니다.


답변

이를 수행하는 가장 좋은 방법은 ‘인식’과 기능을 분리하는 것입니다.

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

}


답변

나는 출판했다 C의 문자열에서 전환을 수행하기 위해 헤더 파일 을 했습니다. 여기에는 스위치와 유사한 동작을 모방하기 위해 strcmp () (또는 이와 유사한)에 대한 호출을 숨기는 매크로 집합이 포함되어 있습니다. Linux에서 GCC로만 테스트했지만 다른 환경을 지원하도록 조정할 수 있다고 확신합니다.

편집 : 요청에 따라 여기에 코드 추가

다음은 포함해야하는 헤더 파일입니다.

#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");
        // break omitted on purpose

    cases("2")
        printf("2 (or 1)\n");
        break;

    defaults
        printf("No match\n");
        break;
} switchs_end;


답변

문자열 검색을 더 빠르게 수행 할 수있는 방법이 있습니다. 가정 : 우리는 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;
}