How to read a key in Go but continue application if no key pressed within x seconds?

huangapple go评论87阅读模式
英文:

How to read a key in Go but continue application if no key pressed within x seconds?

问题

这是从控制台读取按键的简单方法:

reader := bufio.NewReader(os.Stdin)

// ...

func readKey() rune {
    char, _, err := reader.ReadRune()
    if err != nil {
        fmt.Println("Error reading key: ", err)
    }
    return char
}

// ...

fmt.Println("Checking keyboard input...")    

loop:
for {
    keyb := readKey()
    switch keyb {
    case 'x':
        fmt.Println("x key pressed, exiting loop")
        break loop
    }
}

然而,问题是应用程序总是等待读取按键。如果你只想等待5秒钟读取按键,如果没有按键被读取,继续执行应用程序怎么办?

我认为我可能需要引入一个依赖项,比如ncurses或者一个名为crt的turbopascal中的单元(模块),其中有一个readkey函数。但是是否真的需要一个依赖项,还是有一种简单的方法可以做到呢?可能甚至可以使用一些defer()的技巧,我不确定。

英文:

This is an easy way to read a key from the console

reader := bufio.NewReader(os.Stdin)

// ...

func readKey() rune {
	char, _, err := reader.ReadRune()
	if err != nil {
  		fmt.Println("Error reading key: ", err)
	}
	return char
}

// ...

fmt.Println("Checking keyboard input...")    

loop:
for {
	keyb := readKey()
	switch keyb {
	case 'x':
	  fmt.Println("x key pressed, exiting loop")
	  break loop
    }
}

However the issue is the application always waits for a key to be read. What if you want to wait only 5 seconds for a key to be read, and if no key is read, continue the application?

I'm thinking that I must pull in a dependency maybe, such as ncurses or a unit (module) that turbopascal had which was called crt and had a readkey function. But is a dependency really necessary or is there an easy way to do it without? Possibly even some defer() tricks, I don't know.

答案1

得分: 5

你不需要外部依赖来实现这个。

你可以使用一个通道并在其上设置超时。

这里有关于这个的文档信息:https://gobyexample.com/timeouts

关键部分是将输入通过通道传递到一个单独的goroutine中,这样主线程就不会阻塞等待。然后,通过在select语句中设置超时,你可以决定等待多长时间来接收通道中的输入。

以下是使用你的帖子作为基础的一个工作示例:

package main 

import (
	"bufio"
	"os"
	"log"
	"fmt"
	"time"
)

var reader = bufio.NewReader(os.Stdin)

func readKey(input chan rune) {
    char, _, err := reader.ReadRune()
    if err != nil {
        log.Fatal(err)
    }
    input <- char
}

func main() {
    input := make(chan rune, 1)
	fmt.Println("Checking keyboard input...")
    go readKey(input)
	select {
	    case i := <-input:
	        fmt.Printf("Input : %v\n", i)
	    case <-time.After(5000 * time.Millisecond):
	        fmt.Println("Time out!")
	}
}

希望对你有帮助!

英文:

You don't need external dependencies to achieve this.

You can use a channel and set a timeout on it.

Here's documentation info about that: https://gobyexample.com/timeouts

The key part is making the input go through the channel in a separate goroutine, so that the main thread does not block waiting. You can then decide how long to wait to receive the input through the channel by setting a timeout in the select clause.

And here's a working sample using your post as a base:

package main 

import (
	&quot;bufio&quot;
	&quot;os&quot;
	&quot;log&quot;
	&quot;fmt&quot;
	&quot;time&quot;
)

var reader = bufio.NewReader(os.Stdin)

func readKey(input chan rune) {
    char, _, err := reader.ReadRune()
    if err != nil {
        log.Fatal(err)
    }
    input &lt;- char
}

func main() {
    input := make(chan rune, 1)
	fmt.Println(&quot;Checking keyboard input...&quot;)
    go readKey(input)
	select {
	    case i := &lt;-input:
	        fmt.Printf(&quot;Input : %v\n&quot;, i)
	    case &lt;-time.After(5000 * time.Millisecond):
	        fmt.Println(&quot;Time out!&quot;)
	}
}

答案2

得分: 0

可能最“go-isch”的方法是使用goroutine和通道。你可以启动两个goroutine,一个等待输入,另一个在超时后休眠。然后,在主goroutine中使用select语句来检查哪个事件先发生(输入还是超时)。以下是示例代码:

package main

import (
	"fmt"
	"time"
)

func waitForInput(didInput chan<- bool) {
	// 在这里等待有效输入

	didInput <- true
}

func main() {
	didInput := make(chan bool, 1)
	timeout := make(chan bool, 1)

	go func() {
		time.Sleep(5 * time.Second)
		timeout <- true
	}()

	go waitForInput(didInput)

	select {
	case <-didInput:
		fmt.Println("")
		// 在这里继续你的应用程序
	case <-timeout:
		// 输入超时,退出你的应用程序
	}
}

希望对你有帮助!

英文:

Probably the most "go-isch" way to do this is using a goroutine and channels. You start two goroutines, one which waits for input, and one which sleeps until after the timeout period. You then use a select statement in your main goroutine to check what event happened first (the input, or the timeout). Example code:

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func waitForInput(didInput chan&lt;- bool) {
	// Wait for a valid input here

	didInput &lt;- true
}

func main() {
	didInput := make(chan bool, 1)
	timeout := make(chan bool, 1)

	go func() {
		time.Sleep(5 * time.Second)
		timeout &lt;- true
	}()

	go waitForInput(didInput)

	select {
	case &lt;-didInput:
		fmt.Println(&quot;&quot;)
		// Continue your application here
	case &lt;-timeout:
		// Input timed out, quit your application here
	}
}

huangapple
  • 本文由 发表于 2017年5月14日 23:15:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/43965556.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定