Is there function similar to getchar able to handle tab press in console? I want to make some sort of completion in my console app.
问题:
回答1:
C's getchar() example:
#include void main() { char ch; ch = getchar(); printf("Input Char Is :%c",ch); } Go equivalent:
package main import ( "bufio" "fmt" "os" ) func main() { reader := bufio.NewReader(os.Stdin) input, _ := reader.ReadString('\n') fmt.Printf("Input Char Is : %v", string([]byte(input)[0])) // fmt.Printf("You entered: %v", []byte(input)) } The last commented line just shows that when you press tab the first element is U+0009 ('CHARACTER TABULATION').
However for your needs (detecting tab) C's getchar() is not suitable as it requires the user to hit enter. What you need is something like ncurses' getch()/ readline/ jLine as mentioned by @miku. With these, you actually wait for a single keystroke.
So you have multiple options:
Use
ncurses/readlinebinding, for example https://code.google.com/p/goncurses/ or equivalent like https://github.com/nsf/termboxRoll your own see http://play.golang.org/p/plwBIIYiqG for starting point
use
os.Execto run stty or jLine.
refs:
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/zhBE5MH4n-Q
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/S9AO_kHktiY
https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/icMfYF8wJCk
回答2:
Assuming that you want unbuffered input (without having to hit enter), this does the job on UNIX systems:
package main import ( "fmt" "os" "os/exec" ) func main() { // disable input buffering exec.Command("stty", "-F", "/dev/tty", "cbreak", "min", "1").Run() // do not display entered characters on the screen exec.Command("stty", "-F", "/dev/tty", "-echo").Run() // restore the echoing state when exiting defer exec.Command("stty", "-F", "/dev/tty", "echo").Run() var b []byte = make([]byte, 1) for { os.Stdin.Read(b) fmt.Println("I got the byte", b, "("+string(b)+")") } } 回答3:
There are a few wrappers projects around GNU readline, e.g.:
- https://bitbucket.org/binet/go-readline/src
- https://github.com/igoralmeida/readline-go/blob/master/readline.go
but I am not sure, how functional they are. Another way might be terminal emulation, see:
回答4:
Try this:
https://github.com/paulrademacher/climenu/blob/master/getchar.go
The other answers here didn't work, and go-termbox is too heavyweight (it wants to take over the entire terminal window).
回答5:
Thanks goes to Paul Rademacher - this works (at least on Mac):
package main import ( "bytes" "fmt" "github.com/pkg/term" ) func getch() []byte { t, _ := term.Open("/dev/tty") term.RawMode(t) bytes := make([]byte, 3) numRead, err := t.Read(bytes) t.Restore() t.Close() if err != nil { return nil } return bytes[0:numRead] } func main() { for { c := getch() switch { case bytes.Equal(c, []byte{3}): return case bytes.Equal(c, []byte{27, 91, 68}): // left fmt.Println("LEFT pressed") default: fmt.Println("Unknown pressed", c) } } return } 回答6:
1- You may use C.getch():
This works in Windows command line, Reads only one character without Enter:
(Run output binary file inside shell (terminal), not inside pipe or Editor.)
package main //#include import "C" import "fmt" func main() { c := C.getch() fmt.Println(c) } 2- For Linux ( tested on Ubuntu ):
package main /* #include #include #include char getch(){ char ch = 0; struct termios old = {0}; fflush(stdout); if( tcgetattr(0, &old) See:
What is Equivalent to getch() & getche() in Linux?
Why can't I find
3- Also this works, but needs "Enter":
package main import ( "bufio" "fmt" "os" ) func main() { r := bufio.NewReader(os.Stdin) c, err := r.ReadByte() if err != nil { panic(err) } fmt.Println(c) } 回答7:
My solution to this question, though it's really late to the game. Maybe this will help someone else out. I used a C binding like this. I dislike the term mode hacks and prefer a pure code based solution.
#include #include #include #include #include static struct termios g_old_kbd_mode; static int kbhit(void) { struct timeval timeout; fd_set read_handles; int status; FD_ZERO(&read_handles); FD_SET(0, &read_handles); timeout.tv_sec = timeout.tv_usec = 0; status = select(0 + 1, &read_handles, NULL, NULL, &timeout); return status; } int readkey( void ) { int ch; struct termios new_kbd_mode; tcgetattr(0, &g_old_kbd_mode); memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios)); new_kbd_mode.c_lflag &= ~(ICANON | ECHO); new_kbd_mode.c_cc[VTIME] = 0; new_kbd_mode.c_cc[VMIN] = 1; tcsetattr(0, TCSANOW, &new_kbd_mode); while (!kbhit()) { ch = getchar(); tcsetattr(0, TCSANOW, &g_old_kbd_mode); return ch; } tcsetattr(0, TCSANOW, &g_old_kbd_mode); return ch; } Then in Go use it like
package keyboard /* #include "cgetkey.h" */ import "C" const HOME = 149 // DOS Keycode #0 + #71 const END = 152 // DOS Keycode #0 + #79 const PG_UP = 153 // DOS Keycode #0 + #73 const PG_DN = 154 // DOS Keycode #0 + #81 const KEY_UP = 165 // DOS Keycode #0 + #72 const KEY_DN = 166 // DOS Keycode #0 + #80 const KEY_RT = 167 // DOS Keycode #0 + #77 const KEY_LF = 168 // DOS Keycode #0 + #75 /** * * ReadKey * * Reads a single keystroke and returns it's ASCII value. * If a special key is pressed such as an arrow-key, it will process * the ^] + ] return codes and return the ASCII value + 100. * * @return Integer * */ func ReadKey() int { var ch int ch = int(C.readkey()) if ch == 27 { // ESC if int(C.readkey()) == 91 { // [ bracket ch = int(C.readkey()) + 100 // Add 100 to ASCII value. } } return ch } /** * * RawKey * * Reads a single keystroke and returns it's ASCII value. * Does nothing with special keys being pressed. * You will have to read each character code and process them * yourself. * * @return Integer * */ func RawKey() int { return int(C.readkey()) } I created this library here. https://github.com/tlorens/go-ibgetkey
回答8:
You can also use ReadRune:
reader := bufio.NewReader(os.Stdin) // ... char, _, err := reader.ReadRune() if err != nil { fmt.Println("Error reading key...", err) } A rune is similar to a character, as GoLang does not really have characters, in order to try and support multiple languages/unicode/etc.