Come posso confrontare le stringhe in C usando un'istruzione `switch`?


85

In C esiste un switchcostrutto che consente di eseguire diversi rami condizionali di codice in base a un valore intero di prova, ad es.

Come è possibile ottenere lo stesso comportamento (cioè evitare il cosiddetto " if- elseladder") per un valore stringa, cioè a char *?


Cosa intendi per "accendere"?
kennytm

Si prega di riformulare in modo che la domanda chiarisca cosa si sta effettivamente cercando di fare (o chiedere).
Šimon Tóth

9
L'OP probabilmente vuole usare una stringa come parametro di un'istruzione switch. Per quanto ne so, questo non è possibile.
dandan78

Risposte:


104

Se intendi, come scrivere qualcosa di simile a questo:

Quindi la soluzione canonica in C è usare una scala if-else:


1
In realtà, il problema è che ho già un interruttore su int e in un caso speciale ho il valore "B1" e "B2" che voglio usare nello stesso interruttore. L'unico modo è convertire in qualche modo i valori "B1" e "B2" e usarli come int !!?
Niklas

2
@ Niklas: questa è un'informazione importante per la tua domanda. Puoi aggiornare la tua domanda e spiegare (se possibile con del (pseudo-) codice) cosa stai cercando di fare?
Bart van Ingen Schenau

5
@ Niklas: dovresti chiarire la tua domanda: come mai "B1" e "B2" potrebbero essere un caso speciale di int?
Edgar Bonet

1
#define A 1 #define B 2 #define C S1 #define D S2 e questi valori sono ciò che voglio usare nel mio interruttore. Così semplice :-)
Niklas

5
@ Niklas: le definizioni non sono stringhe. Se la definizione è per un numero, puoi usarla direttamente nel tuo interruttore in questo modo switch (something) { case A: /*...*/ break; case B: /*...*/ break; }.
Bart van Ingen Schenau

46

Se hai molti casi e non vuoi scrivere un sacco di strcmp()chiamate, potresti fare qualcosa del tipo:

Devi solo assicurarti che la tua funzione hash non abbia collisioni all'interno del set di possibili valori per la stringa.


9
"assicurati che la tua funzione hash non abbia collisioni all'interno del set di possibili valori per la stringa." - Esiste una tale funzione hash per l'alfabeto [a-zA-Z0-9_]? Qualche esempio?
Arun,

8
@ ArunSaha: Ovviamente non per combinazioni arbitrarie di tali caratteri.
Edgar Bonet

3
Se si utilizzano chiavi stringa di lunghezza fissa, è possibile convertirle ciascuna in numeri interi univoci; nessuna collisione possibile.
Ingegnere

@ ArcaneEngineer Um ... non è questo il problema esatto che la domanda sta cercando di risolvere? In che modo, data solo la stringa, sceglieresti un intero da abbinare? "usa un interruttore o una scala if / else" O forse intendi qualcosa di molto breve come 4 caratteri?
ebyrob

@ebyrob Intendevo qualcosa di paragonabile in un'operazione rapida, come 2 a 64 bit uinti cui bit sono trattati come 8 ASCII da 1 byte char. L'ho implementato qualche tempo fa, per confronti chiave all'interno di una tabella hash in C. In questo modo sradichi la necessità di qualsiasi hashing o bucket. Il problema si presenta quando è necessario superare i 64 bit; quindi paghi il costo per i condizionali mentre ripeti ogni serie di 8 chars nella stringa completa. A meno che non srotoli il ciclo, se conosci la dimensione massima delle chiavi. È un ottimo atto di bilanciamento.
Ingegnere

40

Non c'è modo di farlo in C. Ci sono molti approcci diversi. In genere il più semplice è definire un insieme di costanti che rappresentano le tue stringhe ed eseguire una ricerca per stringa per ottenere la costante:

Ovviamente ci sono modi più efficienti per farlo. Se mantieni ordinate le chiavi, puoi utilizzare una ricerca binaria. Potresti usare anche una tabella hash. Queste cose cambiano le tue prestazioni a scapito della manutenzione.


7
Molto più carino usare un enum invece di un set di #defines per le chiavi, ma per il resto è il meglio che puoi fare.
Craig Ringer

l'incremento non è corretto. lookuptable + i * sizeof (t_symstruct) non è uguale a lookuptable [i].
asdf

@asdf Ecco come funziona l'aritmetica dei puntatori in c. La dimensione è implicita.
ijustlovemath

20

Il mio metodo preferito per farlo è tramite una funzione hash (presa in prestito da qui ). Ciò consente di utilizzare l'efficienza di un'istruzione switch anche quando si lavora con char *:

Naturalmente, questo approccio richiede che i valori hash per tutti i possibili caratteri * accettati siano calcolati in anticipo. Non penso che questo sia un grosso problema; tuttavia, poiché l'istruzione switch opera su valori fissi indipendentemente. È possibile creare un semplice programma per passare char * attraverso la funzione hash e visualizzare i risultati. Questi risultati possono quindi essere definiti tramite macro come ho fatto sopra.


Benvenuto in Stack Overflow. Quello che hai mostrato è ben presentato e una buona idea, ma ... ma non è poi così diverso da alcune delle altre risposte - ce ne sono diverse che utilizzano varianti minori su questa idea. Se aggiungi una nuova risposta a una vecchia domanda stabile, dovresti essere molto sicuro di avere buone nuove informazioni. Questa è principalmente una parola di cautela; Di certo non ho intenzione di votarti per questo.
Jonathan Leffler

16

Penso che il modo migliore per farlo sia separare il 'riconoscimento' dalla funzionalità:


8

Ho pubblicato un file di intestazione per eseguire lo switch sulle stringhe in C. Contiene un insieme di macro che nascondono la chiamata a strcmp () (o simile) per imitare un comportamento simile a uno switch. L'ho testato solo con GCC in Linux, ma sono abbastanza sicuro che possa essere adattato per supportare altri ambienti.

EDIT: aggiunto il codice qui, come richiesto

Questo è il file di intestazione che dovresti includere:

Ed è così che lo usi:


Ho modificato l'esempio non aggiungendo un "break", ma evidenziando il fatto che puoi ometterlo
Andrea Carron

1
questo è più carino! Prima di utilizzare "sscanf" per la corrispondenza, ho imparato "regex.h", che ha molto a che fare con i casi di stringa :)
LinconFive

Che bella soluzione, buona leggibilità e molte più funzionalità rispetto a un interruttore / custodia - Grazie! Non dimenticare "switchs_end:" dopo aver chiuso la parentesi.
Achim

6

C'è un modo per eseguire la ricerca di stringhe più velocemente. Presupposti: dato che stiamo parlando di un'istruzione switch, posso presumere che i valori non cambieranno durante il runtime.

L'idea è di usare qsort e bsearch di C stdlib.

Lavorerò sul codice di xtofl.


6

Per aggiungere alla risposta di Phimueme sopra, se la tua stringa è sempre di due caratteri, puoi creare un int a 16 bit dai due caratteri a 8 bit e attivarlo (per evitare istruzioni switch / case nidificate).


Se lo desideri To add to Phimueme's answer above, sentiti libero di usare la funzione di commento. :)
Onion-Knight

3
@ Onion: noterai che MikeBrom attualmente non ha la reputazione di commentare post diversi dal suo e di rispondere alle sue stesse domande. Detto questo, @Mike "sopra" è scivoloso in SO, perché non esiste un ordinamento affidabile. Meglio collegare alla risposta come "... nella risposta di Phimueme ..." (anche se quella risposta è stata cancellata ora e il collegamento è valido solo per utenti con 10k + reputazione).
dmckee --- gattino ex moderatore

3

Non possiamo sfuggire alla scala if-else per confrontare una stringa con altre. Anche il caso switch regolare è internamente anche un ladder if-else (per interi). Potremmo voler simulare solo il caso dello switch per string, ma non possiamo mai sostituire il ladder if-else. Il migliore degli algoritmi per il confronto delle stringhe non può sfuggire all'utilizzo della funzione strcmp. Significa confrontare carattere per carattere finché non viene trovata una non corrispondenza. Quindi l'uso di if-else ladder e strcmp è inevitabile.

DEMO

E qui ci sono le macro più semplici per simulare il caso dello switch per le stringhe.

E puoi usarli come file

Produzione:

Di seguito è riportato l'utilizzo nidificato di SWITCH:

Produzione:

Ecco la stringa inversa SWITCH, dove puoi usare una variabile (piuttosto che una costante) nella clausola CASE:

Produzione:


"Anche il normale switch-case è internamente anche un if-else ladder (per interi)" non è vero. Se possibile, il compilatore genererà una tabella di salto, che sarà molto più efficiente. Vedi stackoverflow.com/a/14067661/4990392
Dada

2

Questo è generalmente il modo in cui lo faccio.


Interessante. Manca (probabilmente per scelta) la codifica difensiva. E ammiro le parentesi graffe aggiuntive nel caso. Rende il codice molto più leggibile (anche se preferisco le parentesi graffe egiziane per maiuscole e minuscole).
Dariusz

1
BTW, puoi usare espressioni costanti nelle etichette dei casi. case 'B'<<8+'1':lo renderebbe più chiaro, credo, di 0x4231.
Jens

Userei una macro. #define twochar(a) (((uint16_t)a[1]<<8)|a[0])
v7d8dpo4

1

Ecco come lo fai. No, non proprio.


3
Non credo che questo sia standard C.
Johan Kotlinski

2
Fare in modo che la macro supporti l'endianness misto o una funzione viene lasciata come esercizio per il lettore.

2
È standard C, ma non portatile. L'ordine dei byte del carattere multibyte è "dipendente dall'implementazione" e non è necessario che rifletta l'ordine dei byte delle macchine. L'ho usato una volta e mi sono scottato: su Solaris SPARC (big endian) GNU-C 3.4 utilizza un ordine di byte diverso da Sunstudio 12.
Patrick Schlüter

@tristopia Hai ragione ovviamente (per quanto si possa avere dopo aver tentato di fare qualcosa di simile per davvero). Questo è il motivo per cui dovremmo usare tutti B invece.

Perché hai ucciso il tuo account?

1

Se è una stringa di 2 byte puoi fare qualcosa come in questo esempio concreto in cui accendo i codici lingua ISO639-2.

LANIDX_ * è un numero intero costante utilizzato per l'indicizzazione negli array.


0

Assumendo poca endianness e sizeof (char) == 1, potresti farlo (qualcosa del genere è stato suggerito da MikeBrom).

Potrebbe essere generalizzato per il caso BE.


2
Non farlo! Ciò potrebbe causare un'eccezione di "allineamento dei dati". Non è garantito che il char * txt punti a un indirizzo che soddisfa i requisiti di allineamento di int.
Harper

@R ha chiesto quello. @harper non è il caso di x86.
ruslik

Niklas non ha chiesto x86. E poiché hai menzionato il caso big endian, non ti rivolgi esclusivamente all'ambiente x86. In modo che '
Harper

Inoltre, i caratteri multibyte non sono necessariamente nell'ordine dei byte macchina. Vedi il mio commento alla risposta di jbcreix.
Patrick Schlüter

0

I puntatori a funzione sono un ottimo modo per farlo, ad es

result = switchFunction(someStringKey); //result is an optional return value

... questo chiama una funzione che hai impostato con il tasto stringa (una funzione per caso):

Usa un'implementazione hashmap / tabella / dizionario preesistente come khash, restituisci quel puntatore a una funzione all'interno di switchFunction()ed eseguilo (o semplicemente restituiscilo da switchFunction()ed eseguilo tu stesso). Se l'implementazione della mappa non lo memorizza, usa semplicemente un uint64_tinvece che lanci di conseguenza a un puntatore.


@ eri0o Se pensavi che fosse decente, perché non votarlo positivamente? Il downvoter originale è scomparso da tempo.
Ingegnere

-2

Ciao, questo è il modo facile e veloce se hai questo caso:

[Modalità RAPIDA]

[Modalità SPIEGAZIONE]

ad esempio: ho molti menu, ogni scelta nel 1 ° menu ti porta al 2 ° menu, la stessa cosa con il 2 ° menu e il 3 ° menu. ma le Opzioni sono diverse in modo da sapere che l'utente ha scelto definitivamente. esempio:

menu 1: 1 ==> menu 2: 4 ==> menu 3: 2 (...) la scelta è 142. altri casi: 111,141,131,122 ...

soluzione: memorizzare il primo 1 ° in a, 2 ° in b, 3 ° in c. a = 1, b = 4, c = 2

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.