Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - InputNumerico con BackSpace in C
Forum - C/C++ - InputNumerico con BackSpace in C

Pagine: [ 1 2 ] Precedente | Prossimo
Avatar
Carlo (Member)
Guru


Messaggi: 1316
Iscritto: 29/01/2018

Segnala al moderatore
Postato alle 17:27
Domenica, 20/12/2020
Dopo aver visto il codice di AldoBaldo nel post: " Menu rapido con quantita' di voci variabile" e come ha gestito l'input, mi è venuto in mente che ho pubblicato in altro forum una mia funzione da principiante per gestire l'input numerico da tastiera in C che non mostra i caratteri vietati.
La funzione può essere impostata per accettare un numero di cifre massimo definito, anche il valore accettato immesso può essere impostato a solo positivo, o positivo e negativo. Supportato il BackSpace e il taso Esc.

Il codice che ho realizzato mi suscita varie domande:
1) La funzione getch(), non è standard C?, è sbagliato usarla?, come si sostituisce?
2) Per avanzare con l'immissione del prossimo carattere ho usato un goto, per toglierlo devrei mettere un do while, e un if, ma è proprio così brutto il goto?
3) il controllo del sistema operativo Linux/Win funzionerà?
4) Senza stravolgerlo, cosa si può migliorare?
Codice sorgente - presumibilmente C++

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. // scelta libreria per getch(), non testato su Linux
  5. #ifdef __unix__                         // Linux Unix
  6.     #define OS_Windows 0
  7.     #include <curses.h>                 // contiene getch()
  8. #elif defined(_WIN32) || defined(WIN32) // Win 32 64
  9.     #define OS_Windows 1
  10.     #include <conio.h>                  // contiene getch()
  11. #endif
  12.  
  13. /* Funzione InputNumerico: restituisce un intero con segno
  14.    il parametro Cifre,
  15.    stabilisce quante cifre l'utente può immettere.
  16.    Se Cifre è negativo,
  17.    l'utente potrà immettere anche un valore negativo
  18.    accettato anche Esc e BackSpace */
  19.  
  20. int InputNumerico(char Cifre){
  21.  
  22.     unsigned char _ascii=0; // ascii corrente
  23.     int _cifra=0; // valore corrente
  24.     char _negativo=1; // moltiplicatore di segno
  25.     unsigned char _indice=0; // posizione corrente in _sequenza
  26.     unsigned char _sequenza[127]={0}; // cronologia cifre di input
  27. next:
  28.     do{
  29.             _ascii=getch(); // lettura tastiera
  30.             if (_ascii==27){printf("\n"); exit(0);} // Esc
  31.             if (_ascii==8){ // BackSpace
  32.                 if (_indice<1){
  33.                          if (_negativo==-1) printf("\b \b"); // c'è un meno da far scomparire
  34.                         _cifra=0;_negativo=1; // si è tornati in prima posizione: azzeramento
  35.                 }
  36.                 else{
  37.                         _cifra=(_cifra-_sequenza[--_indice])/10; printf("\b \b"); // un passo indietro
  38.                 }
  39.             }
  40.             if (_negativo==1 && _indice==0 && _ascii==45 && Cifre<0){ // meno, solo se Cifre è negativo e si è in prima posizione
  41.                 _negativo=-1; // il numero restituito sarà negativo
  42.                 printf("-"); // visualizzazione
  43.             }
  44.     }while ((_ascii<'0' || _ascii>'9') && _ascii!=13);
  45.  
  46.     if (_ascii==13){ // invio
  47.         return _cifra*_negativo; // ritorno risultato
  48.     }
  49.     else{
  50.         _sequenza[_indice]=_ascii-'0';
  51.         if (_indice<abs(Cifre)){ // controllo limite massimo
  52.             printf("%c",_ascii); // visualizzazione tasto premuto
  53.             _cifra=(_cifra*10)+(_ascii-'0'); // calcolo del risultato
  54.             _indice++;
  55.         }
  56.         goto next;
  57.     }
  58. }
  59.  
  60. int main(){
  61.     char vero=1; // per loop infinito
  62.     char Cifre=-5; // stabilisce il numero di cifre immissibili, se negativo accettato anche il meno
  63.  
  64.     if (Cifre<0) // scelta intestazione
  65.         printf("\n   *** Tasti usabili: meno, cifre, BackSpace, Esc ***\n\n");
  66.     else
  67.         printf("\n   *** Tasti usabili: cifre, BackSpace, Esc ***\n\n");
  68.  
  69.     while(vero){ // loop infinito
  70.         printf(" > Input numerico: ");
  71.         int res=InputNumerico(Cifre); // richiamo funzione
  72.         printf("\n\nAccettato %d \n\n",res); // visualizzazione risultato
  73.     }
  74.     return 0;
  75. }



Ultima modifica effettuata da Carlo il 21/12/2020 alle 9:32


in programmazione tutto è permesso
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 699
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 21:33
Domenica, 20/12/2020
Sai bene che il mio livello di competenza è amatoriale, per cui potrei buttar lì delle inesattezze. Detto questo...

Per quel che ne so e se ho ben capito, getch() non è incluso nello standard, ed ha la particolarità di fornire un carattere immesso da tastiera PRIMA che venga inserito nel buffer di stdin. Per questo permette di analizzare i caratteri BATTUTI alla tastiera e di prendere le volute decisioni PRIMA che il carattere venga acquisito dai meccanismi della console.

Secondo me non è sbagliato usare getch(), semplicemente si tratta di una soluzione che richiede di appoggiarsi a supporti che non è detto che siano disponibili in ogni ambiente di sviluppo. E' senz'altro un meccanismo molto pratico in una quantità di situazioni, e mi stupisco che non si sia ancora provveduto ad inserire qualcosa del genere nello standard "ufficiale".

Il goto io lo uso ogni volta che la situazione mi fa pensare ne valga la pena. In definitiva è un'istruzione come un'altra. Magari chi ne sa di più può fornire qualche spiegazione esoterica sui motivi per i quali invece non è così, ma io sono ignaro di quel tipo di motivazioni, e il goto lo uso. Di rado, ma lo uso.

In merito all'efficacia delle direttive del preprocessore penso che si debba fare riferimento ai file di intestazione di un'implementazione della libreria standard per Linux, e vedere se è effettivamente definito __unix__.

Se ho scritto delle belinate, spero che intervenga nessuno (o altri che se ne intendono) e mi corregga, così imparo qualcosa.


ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
PM Quote
Avatar
Carlo (Member)
Guru


Messaggi: 1316
Iscritto: 29/01/2018

Segnala al moderatore
Postato alle 10:45
Lunedì, 21/12/2020
Grazie, si + o - è quello che avevo letto, chissà se nessuno ci da ulteriori info.
Il goto, anche se non mi dispiaceva, l'ho eliminato, ho usato switch, ora il codice sembra più leggibile e sorpresa... se nel case metto un range:

case '0' ... '9':

gcc lo compila ma mi dice che non è standard, l'ho sostituito con un if:

Codice sorgente - presumibilmente C++

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. // scelta libreria per getch(), non testato su Linux
  5. #ifdef __unix__                         // Linux Unix
  6.     #define OS_Windows 0
  7.     #include <curses.h>                 // contiene getch()
  8. #elif defined(_WIN32) || defined(WIN32) // Win 32 64
  9.     #define OS_Windows 1
  10.     #include <conio.h>                  // contiene getch()
  11. #endif
  12.  
  13. /* Funzione InputNumerico: restituisce un intero con segno
  14.    il parametro Cifre,
  15.    stabilisce quante cifre l'utente può immettere.
  16.    Se Cifre è negativo,
  17.    l'utente potrà immettere anche un valore negativo
  18.    accettato anche Esc e BackSpace */
  19.  
  20. int InputNumerico(char Cifre){
  21.  
  22.     unsigned char _ascii=0; // ascii corrente
  23.     int _cifra=0; // valore corrente
  24.     char _negativo=1; // moltiplicatore di segno
  25.     unsigned char _indice=0; // posizione corrente in _sequenza
  26.     unsigned char _sequenza[127]={0}; // cronologia cifre di input
  27.  
  28.     do{
  29.         _ascii=getch(); // lettura tastiera
  30.  
  31.         switch (_ascii){
  32.  
  33.         case 27: // Esc
  34.             printf("\n"); exit(0);
  35.             break;
  36.  
  37.         case 8: // BackSpace
  38.             if (_indice<1){
  39.                 if (_negativo==-1) printf("\b \b"); // c'è un meno da far scomparire
  40.                 _cifra=0;_negativo=1; // si è tornati in prima posizione: azzeramento
  41.             }
  42.             else{
  43.                 _cifra=(_cifra-_sequenza[--_indice])/10; printf("\b \b"); // un passo indietro
  44.             }
  45.             break;
  46.  
  47.         case 45: // meno
  48.             if (_negativo==1 && _indice==0 && Cifre<0){ // meno, solo se Cifre è negativo e si è in prima posizione
  49.                 _negativo=-1; // il numero restituito sarà negativo
  50.                 printf("-"); // visualizzazione
  51.             }
  52.             break;
  53.  
  54.         default:
  55.             if (_ascii>='0' && _ascii<='9'){ // '0' ... '9' (range su case, non standard C, evitato)
  56.                 _sequenza[_indice]=_ascii-'0'; // cronologia
  57.                 if (_indice<abs(Cifre)){ // controllo limite massimo
  58.                     printf("%c",_ascii); // visualizzazione tasto premuto
  59.                     _cifra=(_cifra*10)+(_ascii-'0'); // calcolo del risultato
  60.                     _indice++;
  61.                 }
  62.             }
  63.             break;
  64.         }
  65.  
  66.     }while (_ascii!=13);
  67.  
  68.     return _cifra*_negativo; // ritorno risultato
  69. }
  70.  
  71. int main(){
  72.     char vero=1; // per loop infinito
  73.     char Cifre=-5; // stabilisce il numero di cifre immissibili, se negativo accettato anche il meno
  74.  
  75.     if (Cifre<0) // scelta intestazione
  76.         printf("\n   *** Tasti usabili: meno, cifre, BackSpace, Esc ***\n\n");
  77.     else
  78.         printf("\n   *** Tasti usabili: cifre, BackSpace, Esc ***\n\n");
  79.  
  80.     while(vero){ // loop infinito
  81.         printf(" > Input numerico: ");
  82.         int res=InputNumerico(Cifre); // richiamo funzione
  83.         printf("\n\nAccettato %d \n\n",res); // visualizzazione risultato
  84.     }
  85.     return 0;
  86. }


Ultima modifica effettuata da Carlo il 21/12/2020 alle 10:55


in programmazione tutto è permesso
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 699
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 14:56
Lunedì, 21/12/2020
Anche a me era venuta in mente la possibilità di ricorrere allo switch, però mi sembrava troppo pedantesco farlo notare, quindi ho evitato.

Se vuoi usare switch per identificare i caratteri numerici, una soluzione c'è:

Codice sorgente - presumibilmente C++

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. // scelta libreria per getch(), non testato su Linux
  5. #ifdef __unix__                         // Linux Unix
  6.     #define OS_Windows 0
  7.     #include <curses.h>                 // contiene getch()
  8. #elif defined(_WIN32) || defined(WIN32) // Win 32 64
  9.     #define OS_Windows 1
  10.     #include <conio.h>                  // contiene getch()
  11. #endif
  12.      
  13. /* Funzione InputNumerico: restituisce un intero con segno
  14.    il parametro Cifre,
  15.    stabilisce quante cifre l'utente può immettere.
  16.    Se Cifre è negativo,
  17.    l'utente potrà immettere anche un valore negativo
  18.    accettato anche Esc e BackSpace */
  19.  
  20. int InputNumerico(char Cifre){
  21.     unsigned char _ascii=0; // ascii corrente
  22.     int _cifra=0; // valore corrente
  23.     char _negativo=1; // moltiplicatore di segno
  24.     unsigned char _indice=0; // posizione corrente in _sequenza
  25.     unsigned char _sequenza[127]={0}; // cronologia cifre di input
  26.      
  27.     do{
  28.         _ascii=getch(); // lettura tastiera
  29.      
  30.         switch (_ascii){
  31.  
  32.         case 27: // Esc
  33.             printf("\n"); exit(0);
  34.             break;
  35.      
  36.         case 8: // BackSpace
  37.             if (_indice<1){
  38.                 if (_negativo==-1) printf("\b \b"); // c'è un meno da far scomparire
  39.                 _cifra=0;_negativo=1; // si è tornati in prima posizione: azzeramento
  40.             }
  41.             else{
  42.                 _cifra=(_cifra-_sequenza[--_indice])/10; printf("\b \b"); // un passo indietro
  43.             }
  44.             break;
  45.  
  46.         case 45: // meno
  47.             if (_negativo==1 && _indice==0 && Cifre<0){ // meno, solo se Cifre è negativo e si è in prima posizione
  48.                 _negativo=-1; // il numero restituito sarà negativo
  49.                 printf("-"); // visualizzazione
  50.             }
  51.             break;
  52.            
  53.         case '0':   // siccome il programma "salta" al case corrispondente al
  54.         case '1':   // valore di _ascii, e da li' prosegue finche' trova un
  55.         case '2':   // break (o a oltranza, se di break non ne trova) qualsiasi
  56.         case '3':   // cifra numerica finisce per portare all'esecuzione delle
  57.         case '4':   // istruzioni che ti interessano
  58.         case '5':
  59.         case '6':
  60.         case '7':
  61.         case '8':
  62.         case '9':
  63.             _sequenza[_indice]=_ascii-'0'; // cronologia
  64.             if (_indice<abs(Cifre)){ // controllo limite massimo
  65.                 printf("%c",_ascii); // visualizzazione tasto premuto
  66.                 _cifra=(_cifra*10)+(_ascii-'0'); // calcolo del risultato
  67.                 _indice++;
  68.             }
  69.             break;
  70.  
  71.         default: // inutile, in questo caso, ma alcuni compilatori
  72.             ;    // si lamentano se non c'e' il default...
  73.         }
  74.      
  75.     }while (_ascii!=13);
  76.      
  77.     return _cifra*_negativo; // ritorno risultato
  78. }
  79.      
  80. int main(){
  81.     char vero=1; // per loop infinito
  82.     char Cifre=-5; // stabilisce il numero di cifre immissibili, se negativo accettato anche il meno
  83.      
  84.     if (Cifre<0) // scelta intestazione
  85.         printf("\n   *** Tasti usabili: meno, cifre, BackSpace, Esc ***\n\n");
  86.     else
  87.         printf("\n   *** Tasti usabili: cifre, BackSpace, Esc ***\n\n");
  88.      
  89.     while(vero){ // loop infinito
  90.         printf(" > Input numerico: ");
  91.         int res=InputNumerico(Cifre); // richiamo funzione
  92.         printf("\n\nAccettato %d \n\n",res); // visualizzazione risultato
  93.     }
  94.     return 0;
  95. }



ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 6380
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 17:12
Lunedì, 21/12/2020
Testo quotato

Postato originariamente da AldoBaldo:

Il goto io lo uso ogni volta che la situazione mi fa pensare ne valga la pena. In definitiva è un'istruzione come un'altra. Magari chi ne sa di più può fornire qualche spiegazione esoterica sui motivi per i quali invece non è così, ma io sono ignaro di quel tipo di motivazioni, e il goto lo uso. Di rado, ma lo uso.




Non me ne intendo ma evitare il goto non ha spiegazioni esoteriche. E' un'istruzione residuata della programmazione "spaghetti" che va contro i principi della programmazione strutturata (per non parlare di quella ad oggetti).

In definitiva non va usata perché, in poche parole, rende non manutenibile il codice (ma parliamo di codici di migliaia e migliaia di righe e manutenzione fatta su altrui progetti). Vorrei vederti cercare un errore in un codice pieno di goto che saltano a destra e sinistra ... Purtroppo chi non ha esperienza non può parlare a ragion veduta.

E' comunque VERO che esiste una (non sopita) diatriba sull'uso MODERATO del goto in situazioni più uniche che rare che, essendo appunto rare, devono essere individuate con certezza e criterio da programmatori molto abili ed esperti.

In tutti i casi, tutto quello che puoi fare con i goto lo puoi fare senza, apparentemente in maniera più "difficile" o "contorta" ma più gestibile nel tempo da parte di più programmatori.

P.S. Con il tempo si affina il codice che si scrive e ci si rende conto delle tante cose che prima si facevano (male) e ci si meraviglia del perché si facessero. E' normale per tutti i programmatori ...

Ad esempio, una scrittura del tipo

Codice sorgente - presumibilmente C/C++

  1. if(...)
  2. {
  3.    return ...
  4. }
  5. else
  6. {
  7.    ... altro codice ...
  8. }
  9. ... ancora altro codice ...



non è affatto chiara e va sostituita con

Codice sorgente - presumibilmente C/C++

  1. if(...)  return ...
  2.  
  3. ... altro codice ...
  4. ... ancora altro codice ...




Ultima modifica effettuata da nessuno il 21/12/2020 alle 17:24


Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti (uno dei padri fondatori del moderno Calcolo delle probabilità) chiamava il gioco del Lotto Tassa sulla stupidità.
PM Quote
Avatar
Carlo (Member)
Guru


Messaggi: 1316
Iscritto: 29/01/2018

Segnala al moderatore
Postato alle 18:47
Lunedì, 21/12/2020
Testo quotato

Postato originariamente da AldoBaldo:

Anche a me era venuta in mente la possibilità di ricorrere allo switch, però mi sembrava troppo pedantesco farlo notare, quindi ho evitato.

Se vuoi usare switch per identificare i caratteri numerici, una soluzione c'è:


Avevo trovato la soluzuione a cascata di case, mi era piaciuto di più l'if, visto che il range è solo uno, invece la soluzione a cascata di case, è adottabile anche per diversi range, è da tenere presente per altre occasioni, grazie.

Testo quotato

Postato originariamente da nessuno:
GOTO:
E' un'istruzione residuata della programmazione "spaghetti" che va contro i principi della programmazione strutturata (per non parlare di quella ad oggetti).

In definitiva non va usata perché, in poche parole, rende non manutenibile il codice (ma parliamo di codici di migliaia e migliaia di righe e manutenzione fatta su altrui progetti). Vorrei vederti cercare un errore in un codice pieno di goto che saltano a destra e sinistra ... Purtroppo chi non ha esperienza non può parlare a ragion veduta.


La voglia è quella di scrivere codice che rispetti il più possibile un'orientamento professionale, anche se i miei piccoli progetti sono destinati a nessun cliente, li vorrei comunque sempre "puliti".
Per il goto, visto che il C, è più vicino alla "macchina" e che l'assembly è pieno di salti jmp e tutti i jxx condizionali mi sono lasciato fuorviare, cercherò di non farlo più.

Testo quotato

Postato originariamente da nessuno:
P.S. Con il tempo si affina il codice che si scrive e ci si rende conto delle tante cose che prima si facevano (male) e ci si meraviglia del perché si facessero. E' normale per tutti i programmatori ...

Ad esempio, una scrittura del tipo

Codice sorgente - presumibilmente C/C++

  1. if(...)
  2. {
  3.    return ...
  4. }
  5. else
  6. {
  7.    ... altro codice ...
  8. }
  9. ... ancora altro codice ...



non è affatto chiara e va sostituita con

Codice sorgente - presumibilmente C/C++

  1. if(...)  return ...
  2.  
  3. ... altro codice ...
  4. ... ancora altro codice ...





Qui sopra hai esposto un concetto, che con le info postate non ho capito, lo puoi spiegare con qualche dettaglio in più?
Non mi sembra che il codice sopra possa essere sostituito da quello sotto...:-?


Ultima modifica effettuata da Carlo il 21/12/2020 alle 19:34


in programmazione tutto è permesso
PM Quote
Avatar
AldoBaldo (Member)
Guru


Messaggi: 699
Iscritto: 08/01/2015

Segnala al moderatore
Postato alle 19:31
Lunedì, 21/12/2020
In effetti sì, perché se si verifica la condizione if la funzione ritorna e quel che segue non viene eseguito in ogni caso. Se invece la condizione if non si verifica, quel che c'è nel blocco else viene eseguito comunque (con o senza l'else stesso) come pure quel che segue.


ATTENZIONE! Sono un hobbista e l'affidabilità delle mie conoscenze informatiche è molto limitata. Non prendere come esempio il codice che scrivo, perché non ho alcuna formazione accademica e rischieresti di apprendere pratiche controproducenti.
PM Quote
Avatar
Carlo (Member)
Guru


Messaggi: 1316
Iscritto: 29/01/2018

Segnala al moderatore
Postato alle 19:45
Lunedì, 21/12/2020
Testo quotato

Postato originariamente da AldoBaldo:

In effetti sì, perché se si verifica la condizione if la funzione ritorna e quel che segue non viene eseguito in ogni caso. Se invece la condizione if non si verifica, quel che c'è nel blocco else viene eseguito comunque (con o senza l'else stesso) come pure quel che segue.



Giusto, c'è return..., credo di operare già così, forse non alla prima stesura, ma in fase di revisione a meno di una svista, il secondo codice è quello che perseguo.


in programmazione tutto è permesso
PM Quote
Avatar
nessuno (Normal User)
Guru^2


Messaggi: 6380
Iscritto: 03/01/2010

Segnala al moderatore
Postato alle 19:50
Lunedì, 21/12/2020
E poi scusate... esiste la isdigit() ...


Ricorda che nessuno è obbligato a risponderti e che nessuno è perfetto ...
---
Il grande studioso italiano Bruno de Finetti (uno dei padri fondatori del moderno Calcolo delle probabilità) chiamava il gioco del Lotto Tassa sulla stupidità.
PM Quote
Pagine: [ 1 2 ] Precedente | Prossimo