PDA

View Full Version : [C/C++] Controllo input tastiera


zanardi84
01-10-2009, 08:19
Salve,
Sono alle prese con una funzione per la creazione di un semplice menu.

Il programma esegue determinate azioni in base alla voce di menu che inserisco.

Gestisco il tutto con uno switch.


void scelta()
{
int scelta;
while ( ( scelta = getchar() ) != EOF ) {


switch ( scelta ) {

case 'S':
case 's':
cout<<"messaggio1"<<endl;
break;

case 'D':
case 'd':
cout<<"messaggio2"<<endl;
break;
case 'M':
case 'm':
cout<<"messaggio3"<<endl;
break;


default:
cout<<"Scelta non corretta. Ridigitare."<<endl;
break;
} /* fine switch */

} /* fine while */
} /* fine funzione */


Funziona, nel senso che se immetto un carattere che non c'entra mi segnala subito l'errore. se digito per esempio S o s mi stampa il messaggio.

Se invece digito SS, cioè 2 volte S mi stampa 2 volte il messaggio. Se digito SD mi stampa i 2 messaggi corrispondenti.

Vorrei, nel modo più semplice possibile (con una funzione che posso richiamare) fare il controllo in modo che se digito 2 caratteri invece di uno mi segnala l'errore.

Qualche idea?

zanardi84
02-10-2009, 10:04
Ho modificato la funzione eliminando le lettere, sostituendole con dei comodi numeri :asd:
Adesso risolve correttamente lo switch, mentre non esce dal while.


using namespace std;
void scelta();
int main(int argc, char *argv[])
{
scelta();
system("PAUSE");
return EXIT_SUCCESS;
}

void scelta()
{
int scelta;
cout<< "Digitare 1 ...."<<endl;
cout<< "Digitare 2 ....."<<endl;
cout<< "Digitare 3 ......."<<endl;
cout<< "Digitare 0 per uscire"<<endl;

cin>>scelta;

/* ripete finchè non si immette 0*/
while ( scelta !=0 ) {

/* azione in base all'input */
switch ( scelta ) {

case 1:
cout<<"mess1"<<endl;

break; /*esce dallo switch */

case 2:
cout<<"mess2"<<endl;

break; /* esce dallo switch */

case 3:
cout<<"mess3"<<endl;

break; /* esce dallo switch */

case '\n': /* ignora gli a capo, */
case '\t': /* tabs, */
case ' ': /* spazi in input */
break; /* esce dallo switch */

default: /* tutti gli altri caratteri */
cout<<"Scelta non corretta. Ridigitare."<<endl;

break;
} /* fine switch */

} /* fine while */
} /* fine funzione */

Pensavo a sta cosa: il while viene chiuso solo se la variabile va a 0. Quindi se nei case poi setto la variabile scelta a 0, il while si trova il nuovo valore a 0 ed esce. Quindi lo fa comunque 2 volte il ciclo.
Se invece è 0 il valore, esce direttamente.
Se il valore non è uno di quelli previsti al momento va in loop. Pensavo quindi: se oltre alla variabile io ne aggiungessi una di status (rendendo quindi la funzione int e non void) che mi viene restituita al main, il quale mi opera un controllo, se status è 0 vuol dire che la funzione è andata bene e ha preso il valore che gli occorreva, se invece è -1 per esempio vuol dire che la funzione ha preso un valore che non andava e deve ricominciare.

Che ne dite come soluzione? Non ci sarebbe alternativamente un modo per evitare di ripartire con la funzione? Per esempio: inserisco il valore che non va bene, lo switch viene risolto, ma il valore è sempre diverso da 0, esce dal while e lo reinizia?

Jecko
02-10-2009, 19:09
personalmente avrei tenuto la prima soluzione per il menù. semplicemente inserisci questa riga dopo lo switch
while(getchar()!="\n");
con questa però mangi tutto l'input che non è stato consumato sia esso corretto o meno. Per segnalare l'errore a quel punto devi aggiustarti con un flag.

zanardi84
06-10-2009, 07:41
Ho rimodificato il programma tornando alla versione iniziale. Ho aggiunto un getchar() che mi evita i loops e ho trasformato ogni digitazione in maiuscolo.

void scelta()
{
int scelta;
while ( ( scelta = getchar() ) != EOF ) {
getchar();
scelta=toupper(scelta);

switch ( scelta ) {


case 'S':
cout<<"messaggio1"<<endl;
break;


case 'D':
cout<<"messaggio2"<<endl;
break;

case 'M':
cout<<"messaggio3"<<endl;
break;


default:
cout<<"Scelta non corretta. Ridigitare."<<endl;
break;
} /* fine switch */

} /* fine while */
} /* fine funzione */

Rimane un problema che non so come risolvere: se inserisco per esempio MM o SD o S e un altro carattere mi stampa:

SS -> messaggio1 messaggio1
SD -> messaggio1 messaggio2
MM -> messaggio3 messaggio3
SG -> messaggio1 e va a capo in attesa di digitazione (non stampa il messaggio del default ma presenta subito il cursore per una ridigitazione).

Qualche idea?

rnuzzo
06-10-2009, 09:01
Pe la variabile scelta dichiara cosi: char scelta = [1];Non so prova e fammi sapere

zanardi84
06-10-2009, 09:17
Levando il secondo getchar() non ci sono problemi a quanto pare, non va in loop. Continua però a dare problemi.

Dichiarando la variabile come char scelta = [1]; ottengo errori.

rnuzzo
06-10-2009, 09:33
Specifica gli errori per favore.

zanardi84
06-10-2009, 09:41
Specifica gli errori per favore.

Ero di fretta e non ho implementato correttamente :D

Adesso, dichiarando come mi hai detto ottengo: initializer fails to determinate size of scelta.

Se invece metto char scelta[1]; e lavoro sulla singola casella accedendo così

while ( ( scelta[0] = getchar() ) != EOF ) {

scelta[0]=toupper(scelta[0]); /* rende maiuscolo il carattere inserito. Includere a inizio programma ctype.h */
/* azione in base all'input */
switch ( scelta[0] ) {
......

è uguale a prima.

Adesso stavo pensando a sta cosa: e se usassi isalpha() della libreria ctype?
Magari si semplifica un attimo. Devo capire prima cosa fa di preciso e cosa prende.

rnuzzo
06-10-2009, 11:56
Io sono niubbo totale e su questo

Adesso stavo pensando a sta cosa: e se usassi isalpha() della libreria ctype?

non saprei come aiutarti.

Ma perche non usi dei semplici 'if'?Cosi risolvi tutto:D

Ma studi su un Deitel?

zanardi84
06-10-2009, 12:57
Io sono niubbo totale e su questo



non saprei come aiutarti.

Ma perche non usi dei semplici 'if'?Cosi risolvi tutto:D

Ma studi su un Deitel?

Sì, su un Deitel :D

rnuzzo
06-10-2009, 14:02
Che capitolo?

Ibrahimovic
06-10-2009, 23:02
Ho rimodificato il programma tornando alla versione iniziale. Ho aggiunto un getchar() che mi evita i loops e ho trasformato ogni digitazione in maiuscolo.

void scelta()
{
int scelta;
while ( ( scelta = getchar() ) != EOF ) {
getchar();
scelta=toupper(scelta);

switch ( scelta ) {


case 'S':
cout<<"messaggio1"<<endl;
break;


case 'D':
cout<<"messaggio2"<<endl;
break;

case 'M':
cout<<"messaggio3"<<endl;
break;


default:
cout<<"Scelta non corretta. Ridigitare."<<endl;
break;
} /* fine switch */

} /* fine while */
} /* fine funzione */

Rimane un problema che non so come risolvere: se inserisco per esempio MM o SD o S e un altro carattere mi stampa:

SS -> messaggio1 messaggio1
SD -> messaggio1 messaggio2
MM -> messaggio3 messaggio3
SG -> messaggio1 e va a capo in attesa di digitazione (non stampa il messaggio del default ma presenta subito il cursore per una ridigitazione).

Qualche idea?
Guarda, è ovvio che reagisce così il tuo programma!!!

La variabile scelta è una variabile char, contiene UN solo carattere alla volta, se tu inserisci due lettere (poniamo S e G ) la getchar attribuirà a scelta prima la S, il tuo switch selezionerà l'operazione, poi il ciclo ricomincera' e la getchar consumerà ancora il buffer attribuendo a scelta la G e eseguendo l'operazione di G.

E' ovvio proprio per struttura del programma, non puoi evitarlo.

Volendo, potresti dichiarare scelta come vettore di caratteri, e printare errore nel caso in cui ci siano piu' di una lettera/operatore e il NULL nella stringa...

Del tipo
if ( strlen( scelta ) > 1 )
printf(" Errore nell'imput bla bla bla \n");



EDIT:

Se proprio vuoi, puoi fare così.
Poniamo che tu la prima lettera che inserisci è sempre la lettera che volevi inserire, e che se ne inserisci due vuoi segnalato l'errore, perchè inserita piu' di una lettera.. ok?

Bene, in un esempio si potrebbe dire che con un imput come questo Snjknsvjhbsdgv noi volevamo intendere "Esegui l'istruzione 'S' " e che dopo la s ci siamo sbagliato e abbiamo digitato qualcosa in piu...

Be, puoi dichiarare scelta come vettore di caratteri, leggere l'imput e usare lo switch infine su scelta[0], ossia sul primo elemento della stringa scelta.

Se vuoi conservare il maiuscolo chiamala in questo modo : switch( toupper( scelta[0] ) ).

Così facendo se la stringa fosse ghbhcd andrai a selezionare l'operazione G dello switch

wingman87
06-10-2009, 23:59
Ma hai provato la soluzione che ti ha proposto Jecko? Mi sembrava corretta...

zanardi84
07-10-2009, 07:58
Dunque, ho notato che la mia funzione è lunghetta, fa più di una cosa ed è troppo specifica. Ho pertanto deciso di crearne una generale che posso chiamare quando mi fa comodo e che fa solo ed esclusivamente il controllo dell'input.

Ibra, secondo te cosa è più elegante? Segnalare subito l'errore o prendere per buono solo il primo carattere della stringa?

cionci
24-10-2009, 09:58
Mi è arrivata una erronea segnalazione da qualcuno, in ogni caso rispondo...

char scelta = [1]; non esiste in C o C++

La variabile char deve essere inizializzata o con un intero o con un carattere fra apici singoli.

La risposta corretta era quella di Jecko, dopo la lettura del primo carattere all'interno della condizione del while, bisogna svuotare il buffer di input:

while(getchar()!="\n");

Questo lo puoi mettere subito dopo l'apertura della parentisi del while.
Una soluzione molto più C++ sarebbe stata questa:

while((scelta = cin.get()) != 0) {
string temp;
getline(cin, temp);