Golang function similar to getchar

匿名 (未验证) 提交于 2019-12-03 01:05:01

问题:

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:

  1. Use ncurses / readline binding, for example https://code.google.com/p/goncurses/ or equivalent like https://github.com/nsf/termbox

  2. Roll your own see http://play.golang.org/p/plwBIIYiqG for starting point

  3. use os.Exec to 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.:

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 on Linux?


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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!