Oppure

Loading
20/12/20 16:27
Carlo
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?
#include <stdio.h>
#include <stdlib.h>

// scelta libreria per getch(), non testato su Linux
#ifdef __unix__                         // Linux Unix
    #define OS_Windows 0
    #include <curses.h>                 // contiene getch()
#elif defined(_WIN32) || defined(WIN32) // Win 32 64
    #define OS_Windows 1
    #include <conio.h>                  // contiene getch()
#endif

/* Funzione InputNumerico: restituisce un intero con segno
   il parametro Cifre,
   stabilisce quante cifre l'utente può immettere.
   Se Cifre è negativo,
   l'utente potrà immettere anche un valore negativo
   accettato anche Esc e BackSpace */

int InputNumerico(char Cifre){

    unsigned char _ascii=0; // ascii corrente
    int _cifra=0; // valore corrente
    char _negativo=1; // moltiplicatore di segno
    unsigned char _indice=0; // posizione corrente in _sequenza
    unsigned char _sequenza[127]={0}; // cronologia cifre di input
next:
    do{
            _ascii=getch(); // lettura tastiera
            if (_ascii==27){printf("\n"); exit(0);} // Esc
            if (_ascii==8){ // BackSpace
                if (_indice<1){
                         if (_negativo==-1) printf("\b \b"); // c'è un meno da far scomparire
                        _cifra=0;_negativo=1; // si è tornati in prima posizione: azzeramento
                }
                else{
                        _cifra=(_cifra-_sequenza[--_indice])/10; printf("\b \b"); // un passo indietro
                }
            }
            if (_negativo==1 && _indice==0 && _ascii==45 && Cifre<0){ // meno, solo se Cifre è negativo e si è in prima posizione
                _negativo=-1; // il numero restituito sarà negativo
                printf("-"); // visualizzazione
            }
    }while ((_ascii<'0' || _ascii>'9') && _ascii!=13);

    if (_ascii==13){ // invio
        return _cifra*_negativo; // ritorno risultato
    }
    else{
        _sequenza[_indice]=_ascii-'0';
        if (_indice<abs(Cifre)){ // controllo limite massimo
            printf("%c",_ascii); // visualizzazione tasto premuto
            _cifra=(_cifra*10)+(_ascii-'0'); // calcolo del risultato
            _indice++;
        }
        goto next;
    }
}

int main(){
    char vero=1; // per loop infinito
    char Cifre=-5; // stabilisce il numero di cifre immissibili, se negativo accettato anche il meno

    if (Cifre<0) // scelta intestazione
        printf("\n   *** Tasti usabili: meno, cifre, BackSpace, Esc ***\n\n");
    else
        printf("\n   *** Tasti usabili: cifre, BackSpace, Esc ***\n\n");

    while(vero){ // loop infinito
        printf(" > Input numerico: ");
        int res=InputNumerico(Cifre); // richiamo funzione
        printf("\n\nAccettato %d \n\n",res); // visualizzazione risultato
    }
    return 0;
}

Ultima modifica effettuata da Carlo 21/12/20 8:32
in programmazione tutto è permesso
20/12/20 20:33
AldoBaldo
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.
21/12/20 9:45
Carlo
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:

#include <stdio.h>
#include <stdlib.h>

// scelta libreria per getch(), non testato su Linux
#ifdef __unix__                         // Linux Unix
    #define OS_Windows 0
    #include <curses.h>                 // contiene getch()
#elif defined(_WIN32) || defined(WIN32) // Win 32 64
    #define OS_Windows 1
    #include <conio.h>                  // contiene getch()
#endif

/* Funzione InputNumerico: restituisce un intero con segno
   il parametro Cifre,
   stabilisce quante cifre l'utente può immettere.
   Se Cifre è negativo,
   l'utente potrà immettere anche un valore negativo
   accettato anche Esc e BackSpace */

int InputNumerico(char Cifre){

    unsigned char _ascii=0; // ascii corrente
    int _cifra=0; // valore corrente
    char _negativo=1; // moltiplicatore di segno
    unsigned char _indice=0; // posizione corrente in _sequenza
    unsigned char _sequenza[127]={0}; // cronologia cifre di input

    do{
        _ascii=getch(); // lettura tastiera

        switch (_ascii){

        case 27: // Esc
            printf("\n"); exit(0);
            break;

        case 8: // BackSpace
            if (_indice<1){
                if (_negativo==-1) printf("\b \b"); // c'è un meno da far scomparire
                _cifra=0;_negativo=1; // si è tornati in prima posizione: azzeramento
            }
            else{
                _cifra=(_cifra-_sequenza[--_indice])/10; printf("\b \b"); // un passo indietro
            }
            break;

        case 45: // meno
            if (_negativo==1 && _indice==0 && Cifre<0){ // meno, solo se Cifre è negativo e si è in prima posizione
                _negativo=-1; // il numero restituito sarà negativo
                printf("-"); // visualizzazione
            }
            break;

        default:
            if (_ascii>='0' && _ascii<='9'){ // '0' ... '9' (range su case, non standard C, evitato)
                _sequenza[_indice]=_ascii-'0'; // cronologia
                if (_indice<abs(Cifre)){ // controllo limite massimo
                    printf("%c",_ascii); // visualizzazione tasto premuto
                    _cifra=(_cifra*10)+(_ascii-'0'); // calcolo del risultato
                    _indice++;
                }
            }
            break;
        }

    }while (_ascii!=13);

    return _cifra*_negativo; // ritorno risultato
}

int main(){
    char vero=1; // per loop infinito
    char Cifre=-5; // stabilisce il numero di cifre immissibili, se negativo accettato anche il meno

    if (Cifre<0) // scelta intestazione
        printf("\n   *** Tasti usabili: meno, cifre, BackSpace, Esc ***\n\n");
    else
        printf("\n   *** Tasti usabili: cifre, BackSpace, Esc ***\n\n");

    while(vero){ // loop infinito
        printf(" > Input numerico: ");
        int res=InputNumerico(Cifre); // richiamo funzione
        printf("\n\nAccettato %d \n\n",res); // visualizzazione risultato
    }
    return 0;
}
Ultima modifica effettuata da Carlo 21/12/20 9:55
in programmazione tutto è permesso
21/12/20 13:56
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'è:

#include <stdio.h>
#include <stdlib.h>
 
// scelta libreria per getch(), non testato su Linux
#ifdef __unix__                         // Linux Unix
    #define OS_Windows 0
    #include <curses.h>                 // contiene getch()
#elif defined(_WIN32) || defined(WIN32) // Win 32 64
    #define OS_Windows 1
    #include <conio.h>                  // contiene getch()
#endif
     
/* Funzione InputNumerico: restituisce un intero con segno
   il parametro Cifre,
   stabilisce quante cifre l'utente può immettere.
   Se Cifre è negativo,
   l'utente potrà immettere anche un valore negativo
   accettato anche Esc e BackSpace */
 
int InputNumerico(char Cifre){
    unsigned char _ascii=0; // ascii corrente
    int _cifra=0; // valore corrente
    char _negativo=1; // moltiplicatore di segno
    unsigned char _indice=0; // posizione corrente in _sequenza
    unsigned char _sequenza[127]={0}; // cronologia cifre di input
     
    do{
        _ascii=getch(); // lettura tastiera
     
        switch (_ascii){
 
        case 27: // Esc
            printf("\n"); exit(0);
            break;
     
        case 8: // BackSpace
            if (_indice<1){
                if (_negativo==-1) printf("\b \b"); // c'è un meno da far scomparire
                _cifra=0;_negativo=1; // si è tornati in prima posizione: azzeramento
            }
            else{
                _cifra=(_cifra-_sequenza[--_indice])/10; printf("\b \b"); // un passo indietro
            }
            break;
 
        case 45: // meno
            if (_negativo==1 && _indice==0 && Cifre<0){ // meno, solo se Cifre è negativo e si è in prima posizione
                _negativo=-1; // il numero restituito sarà negativo
                printf("-"); // visualizzazione
            }
            break;
           
        case '0':   // siccome il programma "salta" al case corrispondente al
        case '1':   // valore di _ascii, e da li' prosegue finche' trova un 
        case '2':   // break (o a oltranza, se di break non ne trova) qualsiasi 
        case '3':   // cifra numerica finisce per portare all'esecuzione delle
        case '4':   // istruzioni che ti interessano
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            _sequenza[_indice]=_ascii-'0'; // cronologia
            if (_indice<abs(Cifre)){ // controllo limite massimo
                printf("%c",_ascii); // visualizzazione tasto premuto
                _cifra=(_cifra*10)+(_ascii-'0'); // calcolo del risultato
                _indice++;
            }
            break;
 
        default: // inutile, in questo caso, ma alcuni compilatori
            ;    // si lamentano se non c'e' il default...
        }
     
    }while (_ascii!=13);
     
    return _cifra*_negativo; // ritorno risultato
}
     
int main(){
    char vero=1; // per loop infinito
    char Cifre=-5; // stabilisce il numero di cifre immissibili, se negativo accettato anche il meno
     
    if (Cifre<0) // scelta intestazione
        printf("\n   *** Tasti usabili: meno, cifre, BackSpace, Esc ***\n\n");
    else
        printf("\n   *** Tasti usabili: cifre, BackSpace, Esc ***\n\n");
     
    while(vero){ // loop infinito
        printf(" > Input numerico: ");
        int res=InputNumerico(Cifre); // richiamo funzione
        printf("\n\nAccettato %d \n\n",res); // visualizzazione risultato
    }
    return 0;
}
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.
21/12/20 16:12
nessuno
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

if(...)
{
   return ...
}
else
{
   ... altro codice ...
}
... ancora altro codice ...


non è affatto chiara e va sostituita con

if(...)  return ...

... altro codice ...
... ancora altro codice ...


Ultima modifica effettuata da nessuno 21/12/20 16: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à.
21/12/20 17:47
Carlo
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.

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ù.

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

if(...)
{
   return ...
}
else
{
   ... altro codice ...
}
... ancora altro codice ...


non è affatto chiara e va sostituita con

if(...)  return ...

... altro codice ...
... 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 21/12/20 18:34
in programmazione tutto è permesso
21/12/20 18:31
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.
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.
21/12/20 18:45
Carlo
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