4

Normally when we call fmt.Scanf() in Golang, the program will read from standard input stream i.e. os.stdin

I want, by Golang code, to make fmt.Scanf() to reading from a file stream - similarly to a technique in Python here.

Please note that we have the piping workaround at command line level; here I'm looking for in-Golang code solution.

If you know how to get there, please share.

p.s.

I guess it requires us to

  • read from file stream and store in a string variable bytes
  • assign bytes to os.stdin stream

Though I'm a Golang newbie and my google search is not very helpful so I asked here.

Nam G VU
  • 33,193
  • 69
  • 233
  • 372

1 Answers1

9

Setting a file as os.Stdin

If this is truly what you want: os.Stdin is a variable (of type *os.File) which you can modify to your liking, you can assign a new value to it. Opening a file and assigning it to os.Stdin, fmt.Scanf() will read directly from that file. For details, see Fill os.Stdin for function that reads from it.

Here's a complete example which opens a file named a.txt, sets it as os.Stdin, then calls fmt.Scanf() to read a string value from os.Stdin (indirectly from the a.txt file). The code below also handles saving and restoring the original value of os.Stdin. It is not needed here, but it serves as an example, and it's also good habit to restore global things you modify temporarily.

f, err := os.Open("a.txt")
if err != nil {
    panic(err)
}
defer f.Close()

oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }()

os.Stdin = f

var s string
if _, err := fmt.Scanf("%s", &s); err != nil {
    fmt.Printf("Error reading: %v", err)
}
fmt.Println("Read:", s)

Note that you can also redirect os.Stdout to a file like:

f2, err := os.Create("out.txt")
if err != nil {
    panic(err)
}
defer f2.Close()

oldStdout := os.Stdout
defer func() { os.Stdout = oldStdout }()

os.Stdout = f2

fmt.Printf("This will be written to the file: %s", f2.Name())

Using fmt.Fscanf()

An easier, better alternative would be to use fmt.Fscanf() which is analogue to fmt.Scanf(), but here you can also pass an io.Reader to read from, and os.File implements io.Reader, so you can directly pass a file to it.

Redirecting a file to the standard input of your app

Another option would be to redirect a file to your app as its standard input when you launch your app. For example:

go run myapp.go < a.txt

This command will start your app, streaming the contents of the a.txt file to the standard input of your app. And so fmt.Scanf() will read the contents of the a.txt file.

icza
  • 389,944
  • 63
  • 907
  • 827
  • I need to stick with `fmt.Scanf()` i.e. the challenges on hackerrank.com do not use `fmt.Fscanf` in their submission template – Nam G VU Sep 25 '17 at 07:31
  • 1
    @NamGVU Then the 1st and 3rd solutions are viable for you. If you can't control how your app is started, then maybe only the 1st. – icza Sep 25 '17 at 07:32
  • And also, I need to stick with non-piping from command line too i.e. need a solution within Golang code – Nam G VU Sep 25 '17 at 07:33
  • 1
    @NamGVU Ok, still, you can use the 1st solution. – icza Sep 25 '17 at 07:33
  • I'll focus on your 1st suggestion - may you give details of how to archive that; I'm so nob that I can't tell myself the which code is correct/best-fit – Nam G VU Sep 25 '17 at 07:34
  • Brilliant answer ever. Accepted a thousand times ^^ – Nam G VU Sep 25 '17 at 07:49