Read multi word string from console - go

I realized that the following only reads a single word string -
fmt.Scan(&sentence)
How do I read multi word string - as in, the string sentence should store a string that contains multiple words.

One can use the InputReader also to scan and print multiple words from the console.
The solution code is as follows:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
inputReader := bufio.NewReader(os.Stdin)
input, _ := inputReader.ReadString('\n')
fmt.Println(input)
}
Console Input:
Let's Go!!!
Console Output:
Let's Go!!!
Note:
To run a GOLANG program, open the command prompt or powershell, navigate to the directory where the program file is present and type in the following command:
go run file_name.go

Your question refers to scanning space separated input. The definition of fmt.Scan https://golang.org/pkg/fmt/#Scan states:
Scan scans text read from standard input, storing successive space-
separated values into successive arguments. Newlines count as space.
It returns the number of items successfully scanned. If that is less
than the number of arguments, err will report why.
So, by definition, input is scanned up until the first space is found. To scan, let's say until you hit a \n on the command line you can use the code from the comment scanning spaces from stdin in Go:
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
return scanner.Text()
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
Also this thread might be useful: https://groups.google.com/forum/#!topic/golang-nuts/r6Jl4D9Juw0

Related

How can I scan a rune?

So far, I haven't been able to print a rune by scanning it with fmt.Scan and printing it with fmt.Print. This is the vary basic code I'm working on:
package main
import "fmt"
func main() {
var c rune
fmt.Scan(&c)
fmt.Printf("%c", c)
}
But it doesn't work, in fact, Printf doesn't produce any output. Instead, by manually assigning a char to my variable c (like var c rune = 'a', without using fmt.Scan), I'm able to print the wanted rune. How can I scan a rune?
As we know Scan return n and err so please check for error under Scan statement as follows
n, err := fmt.Scan(&c)
if err != nil {
fmt.Println(err)
}
It will clearly show you the error and why it was ignored.
Other than the above, please try it locally on your own laptop instead of the playground because on the playground it most of the time gives an EOF error as most of them do not support reading from the terminal.
I hope the above helps you in debugging the issue.
Other Reference:
Scanf ignores if not provided \n

Strange performing of map data type

I am trying to add a bunch of values in a map data type and after that trying to print it out. But it is performing strangely. When I am directly calling the map with the key it is giving me the correct output but not giving me any output when I am storing the key in a variable and then calling it. I am not been able to figure it out what is happening and why am I getting this kind of output. Can Somebody help me with the same.
package main
import (
"bufio"
"fmt"
"os"
)
func main(){
type Authentication struct {
password string
}
var authentication = map[string]Authentication{}
var user1 Authentication
user1.password = "abc"
authentication["def"] = user1
reader := bufio.NewReader(os.Stdin)
usid := readString(reader)
fmt.Println(authentication)
fmt.Println(authentication[usid])
fmt.Println(authentication["def"])
}
// Reading input functions
func readString(reader *bufio.Reader) string {
s, _ := reader.ReadString('\n')
for i := 0; i < len(s); i++ {
if s[i] == '\n' {
return s[:i]
}
}
return s
}
Input:
def
Output:
map[def:{abc}]
{abc}
You're trying to do the same thing twice in readString. But all you have to do is to cut it by one byte.
func readString(reader *bufio.Reader) string {
s, _ := reader.ReadString('\n')
return s[:len(s)-1]
}
The program in the question does not work when \r\n is used as the line terminator in stdin. The program removes the trailing \n from the line, but not the \r.
Fix by using bufio.Scanner instead of bufio.Reader to read lines from the input. The bufio.Scanner type removes line terminators.
func main() {
type Authentication struct {
password string
}
var authentication = map[string]Authentication{}
var user1 Authentication
user1.password = "abc"
authentication["def"] = user1
scanner := bufio.NewScanner(os.Stdin)
if !scanner.Scan() {
log.Fatal(scanner.Err())
}
usid := scanner.Text()
fmt.Println(authentication)
fmt.Println(authentication[usid])
fmt.Println(authentication["def"])
}
There can always be a better way of reading string, but I see your code works too. I ran it in my local and it gives the expected output:
From your description, I presume you are using go playground or any such platform. If that is so, the thing is, go playground doesn't take standard input, and your code has reader on os.Stdin. When I copy your code to playground and add the following line to check,
fmt.Printf("Length of usid: %d\nusid: %q\n", len(usid), usid)
I see the following output:
Length of usid: 0
usid: ""
Conclusion: There is no issue with variables, map or code, but just the stdin.

Getting EOF on 2nd prompt when using a file as Stdin (Golang)

I am trying to do functional testing of a cli app similar to this way.
As the command asks a few input on command prompt, I am putting them in a file and setting it as os.Stdin.
cmd := exec.Command(path.Join(dir, binaryName), "myArg")
tmpfile := setStdin("TheMasterPassword\nSecondAnswer\n12121212\n")
cmd.Stdin = tmpfile
output, err := cmd.CombinedOutput()
The setStdin just creates a tmpFile, write the string in file and returns the *os.File.
Now, I am expecting TheMasterPassword to be first input, and it's working. But for the second input always getting Critical Error: EOF.
The function I am using for asking and getting user input this :
func Ask(question string, minLen int) string {
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("%s: ", question)
response, err := reader.ReadString('\n')
ExitIfError(err)
if len(response) >= minLen {
return strings.TrimSpace(response)
} else {
fmt.Printf("Provide at least %d character.\n", minLen)
}
}
}
Can you please help me to find out what's going wrong?
Thanks a lot!
Adding setStdin as requested
func setStdin(userInput string) *os.File {
tmpfile, err := ioutil.TempFile("", "test_stdin_")
util.ExitIfError(err)
_, err = tmpfile.Write([]byte(userInput))
util.ExitIfError(err)
_, err = tmpfile.Seek(0, 0)
util.ExitIfError(err)
return tmpfile
}
It pretty much looks like in your app your call Ask() whenever you want a single input line.
Inside Ask() you create a bufio.Reader to read from os.Stdin. Know that bufio.Reader–as its name suggests–uses buffered reading, meaning it may read more data from its source than what is returned by its methods (Reader.ReadString() in this case). Which means if you just use it to read one (or some) lines and you throw away the reader, you will throw away buffered, unread data.
So next time you call Ask() again, attempting to read from os.Stdin, you will not continue from where you left off...
To fix this issue, only create a single bufio.Reader from os.Stdin, store it in a global variable for example, and inside Ask(), always use this single reader. So buffered and unread data will not be lost between Ask() calls. Of course this solution will not be valid to call from multiple goroutines, but reading from a single os.Stdin isn't either.
For example:
var reader = bufio.NewReader(os.Stdin)
func Ask(question string, minLen int) string {
// use the global reader here...
}
Also note that using bufio.Scanner would be easier in your case. But again, bufio.Scanner may also read more data from its source than needed, so you have to use a shared bufio.Scanner here too. Also note that Reader.ReadString() returns you a string containing the delimeter (a line ending with \n in your case) which you probably have to trim, while Scanner.Text() (with the default line splitting function) will strip that first before returning the line. That's also a simplification you can take advantage of.

Golang types in net package

I'm coding in golang some tools to make my life easier and I'm not understanding at all how the types in the net package works. This is part of my code:
import (
"bufio"
"fmt"
"log"
"net"
"os"
)
type configFile struct {
gateway, net net.IP
mask, port int
telnetUser, telnetPasswd string
}
var dataConfig configFile
func createConfigFile() {
reader := bufio.NewReader(os.Stdin)
fmt.Println("Let's fill te config file for the application.")
fmt.Println("Which is your gateway IP?")
readGateway, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
dataConfig.gateway = net.ParseIP(readGateway)
if dataConfig.gateway == nil {
log.Fatal("Problem here")
} else {
fmt.Println("Your gateway is: ", dataConfig.gateway.String())
}
}
My problem is the next:
I want to read an IP address from the command line and storage it in the configFile object, which I will user later to create a .json file with all the configuration of my program.
When I read from the command line the IP address the readGateway variable storages it ok, that's expected, but when I try to make
dataConfig.gateway = net.ParseIP(readGateway)
and I try to cast the string object to a net.IP object I'm always getting a nill in the dataConfig.gateway field, so I'm not able to work with that parameter neither convert it to a string.
Could somebody help me?
Thanks in advance.
bufio.Reader.ReadString's docs explain (emphasis mine)
ReadString reads until the first occurrence of delim in the input,
returning a string containing the data up to and including the
delimiter
So readGateway ends up looking like "192.168.1.1\n". Your newline delimiter would not exist in a properly formatted IP address, which means when you parse it with net.ParseIP it's kicking it out as nil.
You can use strings.TrimRight to trim out the newline:
readGateway, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
readGateway = strings.TrimRight(readGateway, "\n")
The documentation of net.ParseIP(s string) states that if the string s provided is not a valid textual representation of an IP address the function will return a nil value, so that must be the case.
Please log the string s before calling net.ParseIP so you can check if you are passing and reading it properly in the program.

Sscanf of multiple string fields in golang

I am trying to use sscanf to parse multiple string fields. Here is an example code snippet:
package main
import "fmt"
func main() {
var name, currency string
_, err := fmt.Sscanf("transaction benson: dollars", "transaction %s: %s", &name, &currency)
fmt.Println(err, name, currency)
}
The output is
input does not match format benson:
Program exited.
%s is greedy and gobbles up to the next space, which means it eats up the colon. After processing the %s, it then tries to scan in the colon, but wait, that’s already been consumed, and the next character is actually a space, not a colon! So it fails.
In C you’d get around this by using %[^:] rather than %s, but it appears Go doesn’t support this. You might need to find some way to parse your string without Sscanf.

Resources