2

I'm trying to read from Stdin in Golang as I'm trying to implement a driver for Erlang. I have the following code:

package main

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

func main() {
    go func() { 
        stdout := bufio.NewWriter(os.Stdin) 
        p := []byte{121,100,125,'\n'}
        stdout.Write(p)
        }()
    stdin := bufio.NewReader(os.Stdin)
    values := make([]byte,4,4)
    for{
        fmt.Println("b")
        if read_exact(stdin) > 0 {
            stdin.Read(values)
            fmt.Println("a")
            give_func_write(values)
        }else{
            continue
        }
    }
}




func read_exact(r *bufio.Reader) int {
    bits := make([]byte,3,3)
    a,_ := r.Read(bits)
    if a > 0 {
        r.Reset(r)
        return 1
    }
    return -1
}

func give_func_write(a []byte) bool {
    fmt.Println("Yahu")
    return true
}

However it seems that the give_func_write is never reached. I tried to start a goroutine to write to standard input after 2 seconds to test this.

What am I missing here? Also the line r.Reset(r). Is this valid in go? What I tried to achieve is simply restart the reading from the beginning of the file. Is there a better way?

EDIT

After having played around I was able to find that the code is stuck at a,_ := r.Read(bits) in the read_exact function

Bula
  • 2,398
  • 5
  • 28
  • 54
  • 1
    See http://stackoverflow.com/a/12361325/357705 – jimt Mar 15 '15 at 13:55
  • @jimt I guess that I will need to have a protocol in which I send a `\n` to make the input work and at the same time discard it when reading it. For testing purposes whats the binary representation of newline? (a better way to phrase this question would be: what is the number that I need to include in my byte slice to have a newline) ? – Bula Mar 15 '15 at 14:36
  • 0x0a should do the trick. – jimt Mar 15 '15 at 14:38
  • @jimt check my new code. `b` only gets printed once. – Bula Mar 15 '15 at 14:53
  • `p := []byte{121,100,125,125,111,100,161,152, 0x0a}` Check out this example: http://play.golang.org/p/QMOPsPWF7t You can replace the `stdin` byte buffer with `os.Stdin` on your own system. The playground doesn't allow use of `os.Stdin`. – jimt Mar 15 '15 at 14:54
  • Or this one, if you don't like the use of strings as messages: http://play.golang.org/p/rhk8liXmJ4 – jimt Mar 15 '15 at 14:58
  • @jimt Right. Looking at your examples I modified the code above to have the newline at the end. I don't want the `read` to ever return hence the reason why I wrote the function as I did. The problem is that it doesn't reach `give_func_write` and what's more it only prints `b` once although it should print it at every iteration of the loop. Is something blocking in this code? I really appreciate your go playground examples and I have used their principle to try to fix my code but it seems as if something is blocking? – Bula Mar 15 '15 at 15:10
  • The code above writes the 3 bytes + '\n' to stdin only once. So it stands to reason it's only read once. The goroutine you spin up at the top does this and then exits. Did you intend for it to have a loop? – jimt Mar 15 '15 at 15:32
  • @jimt it does indeed only write 4 bytes only once however the full code that should be executed, that is to print 'a' and to run 'give_func_write`, doesn't execute even once. – Bula Mar 15 '15 at 16:07
  • You should **never** write to std*in*. – Dave C Mar 15 '15 at 17:35
  • @DaveC is this what's causing that code to not execute? – Bula Mar 16 '15 at 19:20
  • @bula did you get this to work ? Trying to build similar golang driver for erlang but getting "Bad value on output port '#{driver}'" on erlang side :-( – Justin Apr 22 '15 at 15:32

1 Answers1

9

I guess that I will need to have a protocol in which I send a \n to make the input work and at the same time discard it when reading it

No, you don't. Stdin is line-buffered only if it's bound to terminal. You can run your program prog < /dev/zero or cat file | prog.

bufio.NewWriter(os.Stdin).Write(p)

You probably don't want to write to stdin. See "Writing to stdin and reading from stdout" for details.

Well, it's not particular clear for me what you're trying to achieve. I'm assuming, that you just want to read data from stdin by fixed-size chunks. Use io.ReadFull for this. Or if you want to use buffers, you can use Reader.Peek or Scanner to ensure, that specific number of bytes is available. I've changed your program to demonstrate the usage of io.ReadFull:

package main

import (
    "fmt"
    "io"
    "time"
)

func main() {
    input, output := io.Pipe()

    go func() {
        defer output.Close()
        for _, m := range []byte("123456") {
            output.Write([]byte{m})
            time.Sleep(time.Second)
        }
    }()

    message := make([]byte, 3)
    _, err := io.ReadFull(input, message)
    for err == nil {
        fmt.Println(string(message))
        _, err = io.ReadFull(input, message)
    }
    if err != io.EOF {
        panic(err)
    }
}

You can easily split it in two programs and test it that way. Just change input to os.Stdin.

igorw
  • 27,759
  • 5
  • 78
  • 90
alex vasi
  • 5,304
  • 28
  • 31