【问题标题】:Stop looking for user input with 'bufio.NewReader(os.Stdin)'停止使用“bufio.NewReader(os.Stdin)”查找用户输入
【发布时间】:2020-05-18 15:37:36
【问题描述】:

刚接触 golang 和一般编程。我目前正在为学习任务编写一个小测验程序,但遇到了教程未解决的问题,因为我有教程中未包含的功能。

代码如下

func runQuestions(randomize bool) int {
tempqSlice := qSlice //Create temporary set of questions (Don't touch original)
if randomize { //If user has chosen to randomize the question order
    tempqSlice = shuffle(tempqSlice) //Randomize
}

var runningscore int

userinputchan := make(chan string) //Create return channel for user input
go getInput(userinputchan) //Constantly wait for user input

for num, question := range tempqSlice { //Iterate over each question
    fmt.Printf("Question %v:\t%v\n\t", num+1, question.GetQuestion())
    select {
    case <-time.After(5 * time.Second): //
        fmt.Println("-time up! next question-")
        continue
    case input := <-userinputchan:
        if question.GetAnswer() == input { //If the answer is correct
            runningscore++
            continue //Continue to next question
        }
    }

}
return runningscore

}

func getInput(returnchan chan<- string) {
for {
    reader := bufio.NewReader(os.Stdin) //Create reader
    input, _ := reader.ReadString('\n') //Read from
    returnchan <- strings.TrimSpace(input) //Trim the input and send it
}

}

因为问题的规范要求每个问题都有时间限制,所以我设置了一个“无尽的 for loop goroutine”运行,等待用户输入,然后在给出时发送。我的问题很简单:我想在测验结束后停止读者寻找输入,但由于 'reader.ReadString('/n')' 已经在等待输入,我不知道如何。

【问题讨论】:

  • 你不能。直到程序退出为止。
  • 对于程序不同部分的输入,我是否可以简单地将“输入循环”移动到靠近程序顶部并将其通道传递给任何其他需要输入的函数?
  • 根据回答的问题数量和问题的长度创建一个案例。 case amountOfQuestionsAnswered == len(questions): exit

标签: go input channels


【解决方案1】:

一旦测验结束,我想停止读者寻找输入

当读者在寻找输入时,您可以使用在其后台运行的 goroutine 来检查测验计时器是否已过期。

假设您的测验计时器是 30 秒。将计时器传递给 goroutine getInput 并检查计时器是否到期。

var runningscore int
userinputchan := make(chan string) //Create return channel for user input
myTime := flag.Int("timer", 30, "time to complete the quiz")
timer := startTimer(myTime)
go getInput(userinputchan, timer) 

func startTimer(myTime *int) *time.Timer {
    return time.NewTimer(time.Duration(*myTime) * time.Second)
}

func getInput(returnchan chan<- string, timer *time.Timer) {
    for {
        reader := bufio.NewReader(os.Stdin)    //Create reader
        go checkTime(timer)                    
        input, _ := reader.ReadString('\n')    //Read from
        returnchan <- strings.TrimSpace(input) //Trim the input and send it
    }
}

func checkTime(timer *time.Timer) {
    <-timer.C
    fmt.Println("\nYour quiz is over!!!")
    // print the final score
    os.Exit(1)
}

【讨论】:

    【解决方案2】:

    您无法直接执行此操作,但您可以通过从stdin 中逐个字符读取、缓冲输入并仅在用户点击Enter 时发送缓冲数据来解决问题。

    不幸的是,没有标准的逐字符读取包,我将使用github.com/eiannone/keyboard。 (您可以通过go get -u github.com/eiannone/keyboard获取)。

    以下类将是您的输入系统。您可以通过调用NewInput(yourResultChannel) 来实例化它。这将从stdin逐个字符开始读取并将它们缓冲到buf。一旦Enter (char == 0) 被命中,它会将缓冲区的内容发送到resultChannel 端重置缓冲区。

    type Input struct {
        lock     sync.Mutex
        buf      []rune
        resultCh chan string
    }
    
    func NewInput(resultCh chan string) *Input {
        i := &Input{
            resultCh: resultCh,
        }
    
        go i.readStdin()
        return i
    }
    
    func (i *Input) ResetBuffer() {
        i.lock.Lock()
        defer i.lock.Unlock()
    
        i.resetBuffer()
    }
    
    func (i *Input) readStdin() {
        for {
            char, _, err := keyboard.GetSingleKey()
            if err != nil {
                panic(err)
            }
            fmt.Printf("%s", string(char))
    
            i.lock.Lock()
            if char == 0 {
                i.resultCh <- strings.TrimSpace(string(i.buf))
                i.resetBuffer()
            } else {
                i.buf = append(i.buf, char)
            }
            i.lock.Unlock()
        }
    }
    
    func (i *Input) resetBuffer() {
        i.buf = []rune{}
    }
    

    您剩下的唯一工作是在您之前启动 getInput() goroutine 的位置创建 Input 类的实例。

    userinputchan := make(chan string) //Create return channel for user input
    input := NewInput(userinputchan)
    

    并在问题超时时重置缓冲区,以便先前的垃圾输入不会成为新问题答案的一部分。在选择语句中:

    case <-time.After(5 * time.Second):
        fmt.Println("-time up! next question-")
        input.ResetBuffer()
        continue
    

    完整代码:https://play.golang.org/p/-IXVvY9dOZO

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      • 1970-01-01
      相关资源
      最近更新 更多