Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 13

Discussione: [C++] std::cin

  1. #1
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177

    [C++] std::cin

    Cercavo un'alternativa a system("PAUSE"), e mi sono strovato ad affrontare due problemi, uno proprio il creare un qualcosa di multipiattaforma che sostituisse system("PAUSE") e l'altro, la validazione dell'input.
    Il risultato e' riportato sotto ed e' stato ottenuto mettendo assieme quanto trovato ingiro, tentativi vari e documentazione (alla fine qualche autocritica).

    Si accettano, in realta' aspettano, critiche, correzioni, spiegazioni e quant'altro possa migliorare quanto sotto riportato.

    codice:
    void pause()
      {
        std::cout<<"\nPremere INVIO per continuare\n";
        std::string input;
        if(std::cin.fail())//se cin e' in errore, lo si sistema
        {
          std::cin.clear();
        }
        if(std::cin.gcount()!=0)//se cin ha qualche carattere, lo svuoto
        {
          std::cin.ignore(1000,'\n');//1000 andrà sostituito quanto meno con una macro, o ancora meglio con gli standard limits.
                                                  //ho trovato chi consiglia di usare rdbuf()->inavail(), ma non mi funziona...
        }
        while(std::getline(std::cin , input))//in questo modo qualsia input dato con cin, viene tutto riversato in input, quindi cin dovrebbe essere vuoto dopo l'esecuzione del comando
        {
          if (input.length()==0)//l'unico modo per avere input.length()==0 è premere invio senza alcun tasto
          {
            break;
          }
          std::cout<<"\nInput errato\n";
        }
      };
    Successivamente mi sono posto il problema di validare l'input, e sono arrivato a produrre quanto sotto.
    Funziona con int, float, double, char e string.

    Non funziona con gli unsigned int: se infatti inserisco un numero negativo, viene restituito un intero positivo che credo sia il max int a cui sommo con segno il numero immesso (fate una prova usando come par un unsigned int)


    codice:
    template <typename T>
      static void input(T& par)
      {
        std::string input;
        if(std::cin.fail())//se cin e' in errore, lo si sistema
        {
          std::cin.clear();
        }
        if(std::cin.gcount()!=0)//se cin ha qualche carattere, lo svuoto
        {
          std::cin.ignore(1000,'\n');//1000 andrà sostituito quanto meno con una macro, o ancora meglio con gli standard limits.
                                                  //ho trovato chi consiglia di usare rdbuf()->inavail(), ma non mi funziona...
        }
        while(std::getline(std::cin,input))//in questo modo qualsia input dato con cin, viene tutto riversato in input, quindi cin dovrebbe essere vuoto dopo l'esecuzione del comando
        {
          if((typeid(par).name()==typeid('a').name())&&(input.length()==1))//se par e' di tipo char e ho dato un solo tasto di input
          {
            par = input[0];
            break;
          }
          else
          {
            std::stringstream linestream(input);
            if ((linestream >> par)&&(linestream.eof()))//linestream>> dovrebbe restituire errore se passo una lettera a un int
            {
              break;
            }
          }
          std::cout<<"\nInput errato\n";
        }
      };
    Ed ora autocritica:
    - il controllo con gcount e' stato messo perche' se metto come prima riga del programma pause senza prima aver fatto altro input, e metto solo cin.ignore, servono due invio per proseguire, cin.ignore lo ho messo nel caso sia stato usato cin>> in precedenza e siano stati digitati più input di quanti ne servissero (ad esempio se cin>> tipo_char ma immetto 'aaa', devo svuotare perchè mi restano in cin ancora 'aa'.

    - dopo clear, vedo che spesso viene consigliato di usare sync, ma sinceramente non riesco a capire cosa fa, anche perchè qui non usandolo sembra che quanto fatto funzioni.

    - resta aperto il problema degli unsigned int

    - resta aperto il problema, devo ancora pensare/provare, nei casi in cui par potrebbe essere un enum.

    Grazie

  2. #2
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Estremamente al volo.
    Usa sempre:
    codice:
    string line;
    while (getline(cin,line)) {
       ... etc
    
    
    }
    Non usare mai l'operatore >> con cin per acquisire un input da console. In questo modo non invalidi mai l'input e ti eviti tutti quei cin.clear(), cin.ignore() etc.
    Inserisci line in un ostringstream e usa quello per validare l'input.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  3. #3
    Prova a fare così:

    cout<<"premere un tasto per continuare...";
    cin.get();

    o banalmente:

    char x;
    cout<<"Digita qualcosa per continuare";
    cin>>x;

  4. #4
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da alex.catto Visualizza il messaggio
    Prova a fare così:

    cout<<"premere un tasto per continuare...";
    cin.get();

    o banalmente:

    char x;
    cout<<"Digita qualcosa per continuare";
    cin>>x;
    Ecco la mia prova

    codice:
    #include <iostream>
    
    int main()
    {
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();//qui prova a digitare pippo, ovviamente seguito da invio
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();
      std::cout<<"\npremere un tasto per continuare...";
      std::cin.get();
    
    }
    Dato che sono un utonto e non un utente del programma da me fatto, alla prima richiesta di premere un tasto per continuare io decido di digitare pippo e.... fai copia incolla e vedi cosa succede.

    Tutto il giro sopra e' per evitare queste sorprese, anche se va migliorato ancora.

    Grazie Shodan, appena modifico e giungo a qualche nuova conclusione posto nuovamente il codice per ulteriori consigli / correzioni.

    Grazie

  5. #5
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    Estremamente al volo.
    Usa sempre:
    codice:
    string line;
    while (getline(cin,line)) {
       ... etc
    
    
    }
    Non usare mai l'operatore >> con cin per acquisire un input da console. In questo modo non invalidi mai l'input e ti eviti tutti quei cin.clear(), cin.ignore() etc.
    Inserisci line in un ostringstream e usa quello per validare l'input.
    Posso togliere tutti i cin.clear(), cin.ignore() se faccio in modo che da nessuna parte ci sia cin>>..., altrimenti c'e' sempre rischio di trovare cin in 'errore'.

    Ne segue quindi di evitare come la peste cin>>... (cosa che avevi già detto, però che non viene detta ai neofiti da nessuna parte, almeno dove ho guardato io...).

    Stando cosi' le cose la funzione pause potrebbe diventare

    codice:
    void pause()
    {
      std::cout<<"\nPremere INVIO per continuare\n";
      std::string input;
      while(std::getline(std::cin , input)&&(input.length!=0))
      {
        //o fa niente, o messaggio che l'input non e' corretto, o altro
      }
    };
    Per quanto riguarda il resto, non capisco perchè mi consigli di usare ostringstream e non istringstream.

    Ad esempio, per l'input degli int potrebbe essere una cosa del genere (?):

    codice:
    void input(int& par)
      {
        std::string input;
        while(std::getline(std::cin, input))
        {
          std::istringstream linestream(input);
          if ((linestream >> par)&&(linestream.eof()))//se non metto il controllo su eof, 3.56 viene preso come 3
          {
            break;
          }
        }
      };
    @alex.catto: spero che la mia frase dell'utente utonto non ti abbia offeso, non era quella l'intenzione. Volevo semplicemente farti riflettere su come l'utente reale sia ben diverso da un utente ideale.
    Conta che proprio settimana scorsa ho fatto presente un problema di validazione di input dei dati di un software che uso per lavoro (non sono programmatore di professione, sono un perfetto ignorante in materia), dal costo di alcune migliaia di euro, ma che mi scivola sulla buccia di banana di non fare validazione dell'input. Questo mi è costato quasi mezza giornata di lavoro e imprecazioni per scoprire che dove avrei dovuto immettere 0.044, avevo in realta' immesso o.044 (molto spesso non uso il tastierino numerico per immettere i numeri, e sulla tastiera lo zero e la lettera 'o' sono vicini), e il programma bel bello, alla pressione del tasto ok non mi ha dato alcun messaggio, e di sua iniziativa ha posto a zero quel valore...ecco cosa intendo per utonto.

  6. #6
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Quote Originariamente inviata da ing82 Visualizza il messaggio
    Ne segue quindi di evitare come la peste cin>>... (cosa che avevi già detto, però che non viene detta ai neofiti da nessuna parte, almeno dove ho guardato io...).
    In realtà l'input è sempre stato una rogna in ogni linguaggio e C++ non fa eccezione. In genere viene sempre insegnato l'uso di operator >> per comodità/ facilità di apprendimento, ma appena si esce dall'ambito scolastico le cose cambiano drasticamente.
    In realtà operator >> va evitato come la peste se il tipo di dato da acquisire è un char, int, double etc; ossia i tipi di dato nativo (tra i quali, e solo in questo singolo caso, si deve porre anche std::string in quanto operator >> tronca l'acquisizione al primo spazio).

    operator >> in realtà è comodissimo se posto in overload con una classe per acquisire input, ma tale classe poi finirà per inserire i dati dentro int, char, float etc, pertanto si torna nel caso precedente.
    Pertanto per evitare di incasinare l'input (cin) è preferibile acquisire l'input come stringa (std::string preferibilmente) e poi effettuare la validazione dell'input su tale stringa (con istringstream o altri metodi più o meno sofisticati).
    Resta il problema di come segnalare un errore di conversione, ma presumo tu sappia come fare

    Stando cosi' le cose la funzione pause potrebbe diventare
    Meglio
    codice:
    void pause()
    {
        cout << "messaggio" << endl;
        int c;
        do {
            c = cin.get();
        } while (c != '\n' && c != EOF);
    
    };
    Per quanto riguarda il resto, non capisco perchè mi consigli di usare ostringstream e non istringstream.
    Errore mio dovuto al post al volo.

    Ad esempio, per l'input degli int potrebbe essere una cosa del genere (?):
    Così è più flessibile. Tuttavia se chiedi un int e immetti un float, la funzione non fallisce ma fa un cast tra float e int, ossia 3.56 diventa 3, mentre se chiedi un float e inserisci un int il cast e da int a float, ossia 3 diventa 3.000000.
    E' una validazione dell'input un pò blanda, ma evita che inserendo o.004 invalidi cin.
    Se hai bisogno di qualcosa di più sofisticato, istringstream non basta più e serve scriversi la validazione dell'input a "mano".
    p.s. ho messo sia il throw, sia la booleana. Decidi tu cosa lasciare.
    codice:
    template <typename T>
    T read(std::istream& is, bool& valid ) {
        // be optimistic
        valid = true;
        std::string line;
        do {
            getline(is, line);
        } while (line.empty());
    
    
        T data;
    
        istringstream iss(line);
        iss >> data;
        if (!iss) {
            valid = false;
    //        throw std::logic_error("invalid input");
        }    
        return data;
    }
    
    // per simmetria
    template <> 
    std::string read(std::istream& is, bool& valid) {
        valid = true;
        std::string line;
        do {
            getline(is, line);
        } while (line.empty());
        return line;
    }
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  7. #7
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Credo di dover studiare tutta la combricola degli stream, qualche suggerimento di supporto valido (libri, link, ecc)? e se poi, oltre alla teoria, ci scappa anche qualche esempio ben studiato (intendo ovviamente nel materiale che mi verrà indicato, non vi chiedo di farmeli voi), meglio ancora...

    Poi spero di vernirne a capo di sta storia dell'input...

    Grazie

  8. #8
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Cerca Thinking in C++ vol.2 sul sito di Bruce Eckel; contiene un capitolo interamente dedicato agli stream.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

  9. #9
    Utente di HTML.it L'avatar di ing82
    Registrato dal
    Sep 2014
    Messaggi
    177
    Quote Originariamente inviata da shodan Visualizza il messaggio
    codice:
    template <typename T>
    T read(std::istream& is, bool& valid ) {
        // be optimistic
        valid = true;
        std::string line;
        do {
            getline(is, line);
        } while (line.empty());
    
    
        T data;
    
        istringstream iss(line);
        iss >> data;
        if (!iss) {
            valid = false;
    //        throw std::logic_error("invalid input");
        }    
        return data;
    }
    Il dubbio principale e':

    fatico a capire is come parametro: credo possa tornare utile in quanto a read potranno essere passati indifferentemente cin, ifstream e istringstream, in base alle esigenze. Ma sono proprio queste esigenze che non capisco...(sara' la somma di ignoranza e inesperienza, infatti coi file ci ho lavorato pochissimo, praticamente niente, mentre davvero niente con istringstream...)

    Grazie

    Trovato il libro consigliato, sto leggendo, ma l'inglese non mi e' molto d'aiuto nella velocita' di lettura...
    giaà che c'ero ho scaricato anche il volume1, che c'e' pure tradotto in italiano
    Ultima modifica di LeleFT; 28-07-2016 a 10:36

  10. #10
    Utente di HTML.it L'avatar di shodan
    Registrato dal
    Jun 2001
    Messaggi
    2,381
    Se ti confonde puoi usare direttamente cin all'interno della funzione, però così è più "elastica". Anche i file soffrono dello stesso problema di cin quando usati con operator >>, per cui se devi acquisire dati da un file puoi riciclare la funzione.
    This code and information is provided "as is" without warranty of any kind, either expressed
    or implied, including but not limited to the implied warranties of merchantability and/or
    fitness for a particular purpose.

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.