2013-02-01 12 views
5

Oto kwestia, która mnie teraz nurtuje. Kiedy coraz wejście od użytkownika, chcę zatrudnić pętlę zapytać użytkownika, aby ponowić próbę aż wejdą ważny wkład:Jak opróżnić Stdin po fmt.Scanf() w 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)  
} 

uruchamiając powyżej, jeśli użytkownik wprowadzi poprawny wejście, nie ma problemu:

podaj całkowitą:


kod zakończenia 0 proces zakończony normalnie.

Ale spróbuj wpisać ciąg znaków zamiast?

Proszę podać liczbę całkowitą: co?
Przepraszamy, nieprawidłowe dane wejściowe. Podaj liczbę całkowitą:
Przepraszamy, nieprawidłowe dane wejściowe. Podaj liczbę całkowitą:
Przepraszam ...

Etc, i kontynuuje zapętlenie znaku po znaku, aż ciąg znaków zostanie wyczerpany. Nawet wprowadzając pętle z jednym znakiem dwukrotnie, zakładam, że parsuje znak nowej linii.

W każdym razie, musi być sposób na opróżnienie Stdin in Go?

P.S. W przypadku braku takiej funkcji, w jaki sposób obejść ją, aby zapewnić równoważną funkcjonalność? Nawet mi się to nie udało ...

Odpowiedz

3

Naprawię to, czytając do końca wiersza po każdym niepowodzeniu. Spowoduje to wyczyszczenie reszty tekstu.

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

To jest dobre obejście tego problemu. Dzięki! –

+0

Po prostu użyj 'Scanln', który jest udokumentowany jako zużywający białe spacje i czytający aż do nowej linii. –

1

wiem, że to już odpowiedział, ale to była moja realizacja:

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

To powinno działać w każdej sytuacji, w tym te, gdzie „stdin.ReadString («\ n»)” nie może być używany.

1

Czy to źle, aby obudzić stare pytanie?

Preferuję używać fmt.Scanln, ponieważ A) nie wymaga importowania innej biblioteki (np. Czytnika) i B) nie wymaga jawnej pętli for.

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) 
} 

Wygląda jednak na to, że powinno być bardziej eleganckie rozwiązanie. Szczególnie w przypadku fmt.Scanln wydaje się dziwne, że odczyt zatrzymuje się po pierwszym nielicznym bajcie, a nie "skanuje linię".

1

Wpadłem na podobny problem, by uzyskać informacje od użytkownika, ale rozwiązałem je w nieco inny sposób. Dodanie do wątku, w przypadku gdy ktoś inny znajdzie to przydatne:

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

Przepraszamy za kopanie to z powrotem w górę, ale wpadłem na to dziś i chciał poprawić istniejące odpowiedzi za pomocą nowych funkcji standardowej biblioteki.

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 
} 

Podstawowym założeniem jest to, aby zawsze buforować swój czyta ze standardowego wejścia. Gdy podczas skanowania wystąpi błąd, po prostu odrzuć zawartość bufora. W ten sposób zaczniesz od pustego bufora do następnego skanowania.

Alternatywnie, można odrzucić bufor przed skanowaniem, więc wszelkie zbłąkane dane wejściowe użytkownika przed nim nie zostaną pobrane.

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 
}