2013-02-01 12 views
5

Ecco un problema che mi tormenta in questo momento. Quando si ottiene input da parte dell'utente, voglio impiegare un ciclo per chiedere all'utente di riprovare fino a quando non entrano input valido:Come svuotare Stdin dopo fmt.Scanf() in Go?

// user_input.go 
package main 

import (
    "fmt" 
) 

func main() { 
    fmt.Println("Please enter an integer: ") 

    var userI int 

    for { 
     _, err := fmt.Scanf("%d", &userI) 
     if err == nil { 
      break 
     } 
     fmt.Println("Sorry, invalid input. Please enter an integer: ") 
    } 

    fmt.Println(userI)  
} 

esecuzione quanto sopra, se l'utente inserisce input valido, nessun problema:

Inserisci un numero intero:


codice di uscita 0, processo terminato normalmente.

Ma provare a inserire una stringa?

Immettere un numero intero: cosa?
Spiacente, input non valido. Per favore inserisci un numero intero:
Siamo spiacenti, input non valido. Per favore inserisci un numero intero:
Spiacente ...

Ecc, e continua a scorrere carattere per carattere finché la stringa non è esaurita. Anche inserendo due volte un singolo carattere, presumo mentre analizza la nuova riga.

In ogni caso, deve esistere un modo per svuotare Stdin in Go?

P.S. In assenza di una tale funzionalità, come faresti a lavorarci intorno per fornire funzionalità equivalenti? Ho fallito anche in questo ...

risposta

3

Fisserei questo leggendo fino alla fine della riga dopo ogni errore. Questo cancella il resto del testo.

package main 

import (
    "bufio" 
    "fmt" 
    "os" 
) 

func main() { 
    stdin := bufio.NewReader(os.Stdin) 

    fmt.Println("Please enter an integer: ") 

    var userI int 

    for { 
     _, err := fmt.Fscan(stdin, &userI) 
     if err == nil { 
      break 
     } 

     stdin.ReadString('\n') 
     fmt.Println("Sorry, invalid input. Please enter an integer: ") 
    } 

    fmt.Println(userI) 
} 
+0

Questa è una buona soluzione. Grazie! –

+0

Basta usare 'Scanln' che è documentato come spazio bianco consumante e lettura fino alla nuova riga. –

1

So che questo è già stato risposto, ma questa era la mia realizzazione:

func flush (reader *bufio.Reader) { 
    var i int 
    for i = 0; i < reader.Buffered(); i++ { 
     reader.ReadByte() 
    } 
} 

questo dovrebbe funzionare in ogni situazione, comprese quelle dove "stdin.ReadString ('\ n')" non può essere Usato.

1

È brutto svegliare una vecchia domanda?

Preferisco usare fmt.Scanln perché A) non richiede l'importazione di un'altra libreria (ad es. Lettore) e B) non implica un ciclo for esplicito.

func someFunc() { 
    fmt.Printf("Please enter an integer: ") 

    // Read in an integer 
    var i int 
    _, err := fmt.Scanln(&i) 
    if err != nil { 
      fmt.Printf("Error: %s", err.Error()) 

      // If int read fails, read as string and forget 
      var discard string 
      fmt.Scanln(&discard) 
      return 
    } 
    fmt.Printf("Input contained %d", i) 
} 

Tuttavia, sembra che ci dovrebbe essere una soluzione più elegante. Soprattutto nel caso di fmt.Scanln sembra strano che la lettura si interrompa dopo il primo byte non numerico anziché "scansionare la linea".

1

Mi sono imbattuto in un problema simile per ottenere input da parte dell'utente, ma l'ho risolto in un modo leggermente diverso. Aggiungendo alla discussione nel caso in cui qualcun altro trova questo utile:

package main 

import (
    "bufio" 
    "fmt" 
    "os" 
    "strings" 
) 

// Get first word from stdin 
func getFirstWord() (string) { 
    input := bufio.NewScanner(os.Stdin) 
    input.Scan() 
    ans := strings.Fields(input.Text()) 

    if len(ans) == 0 { 
     return "" 
    } else { 
     return ans[0] 
    } 
} 

func main() { 
    fmt.Printf("Would you like to play a game?\n> ") 
    ans := getFirstWord() 
    fmt.Printf("Your answer: %s\n", ans) 
} 
0

spiacenti per scavare questo back up, ma mi sono imbattuto in questo oggi e voleva migliorare le risposte esistenti utilizzando nuove funzionalità della libreria standard.

import (
    "bufio" 
    "fmt" 
    "os" 
) 

func discardBuffer(r *bufio.Reader) { 
    r.Discard(r.Buffered()) 
} 

stdin := bufio.NewReader(os.Stdin) 
var i int 
for true { 
    if _, err := fmt.Fscanln(stdin, &i); err != nil { 
     discardBuffer(stdin) 
     // Handle error, display message, etc. 
     continue 
    } 
    // Do your other value checks and validations 
    break 
} 

L'idea di base è di bufferizzare sempre le tue letture dallo stdin. Quando si verifica un errore durante la scansione, basta eliminare il contenuto del buffer. In questo modo inizi con un buffer vuoto per la tua prossima scansione.

In alternativa, è possibile scartare il buffer prima di eseguire la scansione, in modo tale che eventuali input indesiderati dell'utente prima di allora non vengano rilevati.

func fscanln(r *bufio.Reader, a ...interface{}) error { 
    r.Discard(r.Buffered()) 
    _, err := fmt.Fscanln(r, a...) 
    return err 
} 

stdin := bufio.NewReader(os.Stdin) 
var i int 
if err := fscanln(stdin, &i); err != nil { 
    // Handle error 
}