Wie kann ich Strings in C mit einer `switch`-Anweisung vergleichen?

Lesezeit: 8 Minuten

Benutzeravatar von Niklas
Niklas

In C gibt es ein switch Konstrukt, das es einem ermöglicht, verschiedene bedingte Codezweige basierend auf einem ganzzahligen Testwert auszuführen, z.

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

Wie ist es möglich, das gleiche Verhalten zu erreichen (d. h. das sogenannte “ifelse Leiter”) für einen Zeichenfolgenwert, dh a char *?

  • Was meinst du mit “einschalten”?

    – kennytm

    25. Oktober 2010 um 13:12 Uhr

  • Bitte formulieren Sie so um, dass die Frage deutlich macht, was Sie tatsächlich versuchen (oder fragen).

    – Simon Toth

    25. Oktober 2010 um 13:13 Uhr

  • Das OP möchte wahrscheinlich eine Zeichenfolge als Parameter einer Schalteranweisung verwenden. Soweit ich weiß, ist dies nicht möglich.

    – dandan78

    25. Oktober 2010 um 13:14 Uhr

  • Sie können Strings nicht in switch-Anweisungen verwenden, aber um zu vermeiden, dass ein String nach dem anderen mit einer naiven Else-If-Leiter geprüft wird, schlage ich vor: a binäre Suche

    – Benutzer16217248

    1. März um 17:08 Uhr

Benutzeravatar von Bart van Ingen Schenau
Bart van Ingen-Schenau

Wenn Sie meinen, wie man etwas Ähnliches schreibt:

// switch statement
switch (string) {
  case "B1": 
    // do something
    break;
  /* more case "xxx" parts */
}

Dann besteht die kanonische Lösung in C darin, eine if-else-Leiter zu verwenden:

if (strcmp(string, "B1") == 0) 
{
  // do something
} 
else if (strcmp(string, "xxx") == 0)
{
  // do something else
}
/* more else if clauses */
else /* default: */
{
}

  • Eigentlich ist das Problem, dass ich bereits einen Schalter auf int habe und in einem besonderen Fall habe ich die Werte “B1” und “B2”, die ich im selben Schalter verwenden möchte. Die einzige Möglichkeit besteht darin, die Werte “B1” und “B2” irgendwie umzuwandeln und sie als int zu verwenden !!?

    – Niklas

    25. Oktober 2010 um 13:26 Uhr

  • @Niklas: Das sind wichtige Informationen für deine Frage. Können Sie Ihre Frage aktualisieren und erklären (wenn möglich mit etwas (Pseudo-) Code), was Sie versuchen?

    – Bart van Ingen Schenau

    25. Oktober 2010 um 13:32 Uhr

  • @Niklas: Sie sollten Ihre Frage klären: Wie um alles in der Welt könnten “B1” und “B2” ein Sonderfall eines int sein?

    – Edgar Bonett

    25. Oktober 2010 um 13:35 Uhr


  • #define A 1 #define B 2 #define C S1 #define D S2 und diese Werte möchte ich in meinem Schalter verwenden. So einfach 🙂

    – Niklas

    25. Oktober 2010 um 19:15 Uhr

  • @Niklas: Defines sind keine Strings. Wenn das Define für eine Zahl ist, können Sie es direkt in Ihrem Switch verwenden switch (something) { case A: /*...*/ break; case B: /*...*/ break; }.

    – Bart van Ingen Schenau

    26. Oktober 2010 um 8:54 Uhr

Wenn Sie viele Fälle haben und nicht viele schreiben möchten strcmp() Anrufe, könnten Sie so etwas tun:

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

Sie müssen nur sicherstellen, dass Ihre Hash-Funktion keine Kollisionen innerhalb des Satzes möglicher Werte für die Zeichenfolge aufweist.

  • “Stellen Sie sicher, dass Ihre Hash-Funktion keine Kollisionen innerhalb des Satzes möglicher Werte für die Zeichenfolge hat.” — Gibt es eine solche Hash-Funktion für das Alphabet? [a-zA-Z0-9_]? Irgendein Beispiel?

    – Arun

    26. Oktober 2010 um 4:17 Uhr


  • @ArunSaha: Offensichtlich nicht für beliebige Kombinationen von solchen Charakteren.

    – Edgar Bonett

    26. Oktober 2010 um 6:21 Uhr

  • Wenn Sie Zeichenfolgenschlüssel mit fester Länge verwenden, können Sie sie jeweils in eindeutige Ganzzahlen konvertieren. keine Kollisionen möglich.

    – Techniker

    16. September 2015 um 8:41 Uhr


  • @ebyrob Ich meinte alles Vergleichbare in einer schnellen Operation, wie z. B. 2 64-Bit uints, deren Bits als 8 1-Byte ASCII behandelt werden chars. Ich habe dies vor einiger Zeit für Schlüsselvergleiche innerhalb einer Hash-Tabelle in C implementiert. Sie beseitigen somit die Notwendigkeit von Hashing oder Buckets. Das Problem tritt auf, wenn Sie 64 Bit überschreiten müssen; Sie zahlen dann die Kosten für Bedingungen, während Sie jeden Satz von 8 durchlaufen chars in der vollständigen Zeichenfolge. Es sei denn, Sie rollen die Schleife aus, wenn Sie die maximale Größe der Schlüssel kennen. Es ist ein feiner Balanceakt.

    – Techniker

    2. Februar 2018 um 16:34 Uhr


  • Eine brillante Umsetzung davon, ohne den Hash manuell zu berechnen: heeden.nl/statichashc.htm

    – lkanab

    21. März 2021 um 8:27 Uhr


Benutzeravatar von plinth
Sockel

In C gibt es dafür keine Möglichkeit. Es gibt viele verschiedene Ansätze. In der Regel ist es am einfachsten, eine Reihe von Konstanten zu definieren, die Ihre Zeichenfolgen darstellen, und eine Suche nach Zeichenfolge durchzuführen, um die Konstante zu erhalten:

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

Es gibt natürlich effizientere Methoden, dies zu tun. Wenn Sie Ihre Schlüssel sortiert halten, können Sie eine binäre Suche verwenden. Sie können auch eine Hashtabelle verwenden. Diese Dinge verändern Ihre Leistung auf Kosten der Wartung.

  • Viel schöner ist es, eine Aufzählung anstelle einer Reihe von #defines für die Schlüssel zu verwenden, aber ansonsten das Beste, was Sie tun können.

    – Craig Ringer

    16. September 2015 um 4:17 Uhr

  • die Erhöhung ist falsch. lookuptable + i*sizeof(t_symstruct) ist nicht gleich lookuptable[i].

    – asdf

    7. Februar 2019 um 1:20 Uhr

  • @asdf So funktioniert die Zeigerarithmetik in c. Die Größe von ist implizit.

    – Ich liebe Mathe

    11. April 2020 um 22:28 Uhr

  • ist es schneller als fest codiertes memcmp() ?

    Benutzer15307601

    30. Oktober 2021 um 21:57 Uhr

Benutzeravatar von Matthew Rasa
Matthäus Rasa

Meine bevorzugte Methode dafür ist eine Hash-Funktion (von hier entlehnt). Dadurch können Sie die Effizienz einer switch-Anweisung auch dann nutzen, wenn Sie mit char * arbeiten:

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

Dieser Ansatz erfordert natürlich, dass die Hash-Werte für alle möglichen akzeptierten Zeichen * im Voraus berechnet werden. Ich denke nicht, dass dies ein zu großes Problem ist; da die switch-Anweisung jedoch unabhängig davon mit festen Werten arbeitet. Ein einfaches Programm kann erstellt werden, um char * durch die Hash-Funktion zu leiten und ihre Ergebnisse auszugeben. Diese Ergebnisse können dann über Makros definiert werden, wie ich es oben getan habe.

Ich denke, der beste Weg, dies zu tun, besteht darin, die “Erkennung” von der Funktionalität zu trennen:

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

}

Ich habe eine veröffentlicht Header-Datei um den Schalter an den Zeichenfolgen in C auszuführen. Es enthält eine Reihe von Makros, die den Aufruf von strcmp() (oder ähnlichem) verbergen, um ein Schalter-ähnliches Verhalten nachzuahmen. Ich habe es nur mit GCC unter Linux getestet, aber ich bin mir ziemlich sicher, dass es angepasst werden kann, um andere Umgebungen zu unterstützen.

BEARBEITEN: Hier wurde der Code wie gewünscht hinzugefügt

Dies ist die Header-Datei, die Sie einfügen sollten:

#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 *ss__sw = (x); bool ss__done = false; bool ss__cont = false; \
        regex_t ss__regex; regcomp(&ss__regex, ".*", 0); do {

/** Check if the string matches the cases argument (case sensitive) */
#define cases(x)    } if ( ss__cont || !strcmp ( ss__sw, x ) ) \
                        { ss__done = true; ss__cont = true;

/** Check if the string matches the icases argument (case insensitive) */
#define icases(x)    } if ( ss__cont || !strcasecmp ( ss__sw, x ) ) { \
                        ss__done = true; ss__cont = true;

/** Check if the string matches the specified regular expression using regcomp(3) */
#define cases_re(x,flags) } regfree ( &ss__regex ); if ( ss__cont || ( \
                              0 == regcomp ( &ss__regex, x, flags ) && \
                              0 == regexec ( &ss__regex, ss__sw, 0, NULL, 0 ) ) ) { \
                                ss__done = true; ss__cont = true;

/** Default behaviour */
#define defaults    } if ( !ss__done || ss__cont ) {

/** Close the switchs */
#define switchs_end } while ( 0 ); regfree(&ss__regex); }

#endif // __SWITCHS_H__

Und so verwenden Sie es:

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;

Benutzeravatar von Dariusz
Dariusz

Es gibt eine Möglichkeit, die Zeichenfolgensuche schneller durchzuführen. Annahmen: Da es sich um eine switch-Anweisung handelt, kann ich davon ausgehen, dass sich die Werte zur Laufzeit nicht ändern.

Die Idee ist, die qsort und bsearch der C stdlib zu verwenden.

Ich werde am Code von xtofl arbeiten.

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

1421540cookie-checkWie kann ich Strings in C mit einer `switch`-Anweisung vergleichen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy