ChatGPT解决这个技术问题 Extra ChatGPT

How can I read from standard input in the console?

go

I would like to read standard input from the command line, but my attempts have ended with the program exiting before I'm prompted for input. I'm looking for the equivalent of Console.ReadLine() in C#.

This is what I currently have:

package main

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

func main() {
    reader := bufio.NewReader(os.Stdin)
    fmt.Print("Enter text: ")
    text, _ := reader.ReadString('\n')
    fmt.Println(text)

    fmt.Println("Enter text: ")
    text2 := ""
    fmt.Scanln(text2)
    fmt.Println(text2)

    ln := ""
    fmt.Sscanln("%v", ln)
    fmt.Println(ln)
}
This code looks correct. Out of curiosity, are you running this on the Playground? The Go Playground doesn't allow stdin input due to networking reasons.
Nevermind, it looks to be a subtle issue where you needed a pointer (see my answer). Though I'm not sure what the problem with the bufio.NewReader method is since it works for me.
possible duplicate of Read from initial stdin in GO?
Don't mix bufio buffering of any reader (e.g. bufio.NewReader(os.Stdin)) with direct reads from the underlining reader (e.g. fmt.Scanln(x) directly reads from os.Stdin). Buffering may read arbitrarily far ahead. (In this specific case the later should be fmt.Fscanln(reader,x) to read from the same buffer).
I don't get fmt.Sscanln works, it becomes "%v" after running

L
Linear

I'm not sure what's wrong with the block

reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
fmt.Println(text)

As it works on my machine. However, for the next block you need a pointer to the variables you're assigning the input to. Try replacing fmt.Scanln(text2) with fmt.Scanln(&text2). Don't use Sscanln, because it parses a string already in memory instead of from stdin. If you want to do something like what you were trying to do, replace it with fmt.Scanf("%s", &ln)

If this still doesn't work, your culprit might be some weird system settings or a buggy IDE.


Are those supposed to be single quotes? ReadString('\n') or ReadString("\n")?
@425nesp yes, that's the delimeter, which is a single byte. golang.org/pkg/bufio/#Reader.ReadString
Good answer, but this fails when I try using backspace, etc keys
So much for Golang to read a line from file via reader rd to variable s as if s,_ = rd.ReadString('\n'); true { s = strings.Trim(s, " \n") }
Just sharing an interesting thing (I'm a Golang beginner): \n must be inside single quotes (don't try to use double quotes). Or else, it'll reproduce this: cannot use "\n" (type string) as type byte in argument to reader.ReadString
P
Peter Mortensen

You can as well try:

scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}

if scanner.Err() != nil {
    // Handle error.
}

You can remove the "for {}" if you just want one a one line input.
if there a for {} loop, how to come out from loop as you enter? Is there a special character that will make for loop to stop? - Thanks
@Madhan scanner.Scan() returns bool value to indicate exiting the for loop or not.
You will get this error bufio.Scanner: token too long If your input is bigger than 64 * 1024 bytes. Also don't forget add fmt.Println(scanner.Err())below the for loop.
We cannot increase the limit. It is hardcoded into the go runtime.
C
Community

I think a more standard way to do this would be:

package main

import "fmt"

func main() {
    fmt.Print("Enter text: ")
    var input string
    fmt.Scanln(&input)
    fmt.Print(input)
}

Take a look at the scan godoc: http://godoc.org/fmt#Scan

Scan scans text read from standard input, storing successive space-separated values into successive arguments. Newlines count as space. Scanln is similar to Scan, but stops scanning at a newline and after the final item there must be a newline or EOF.


This doesn't seem to like spaces in the input string.
@HairyChris yes this is strange. In the doc it says that stops scanning at a newline and after the final item there must be a newline or EOF so not sure why space "breaks" it... I guess it is a bug
There was a bug opened for this: github.com/golang/go/issues/5703 It got closed as WorkingAsIntended. See also: stackoverflow.com/questions/24005899/… and groups.google.com/forum/#!topic/golang-nuts/r6Jl4D9Juw0 Seems a lot of people have issues with this. Documentation change needed? Also, from that last link: "Scan and Scanln are for parsing and stuff like that, so just getting a single line of text from stdin would defeat the purpose."
To me, its really confusing that fmt.Scan in any of its similar functions don't play well with spaces like the bufio.NewReader does.
The same problem with spaces remains while using fmt.Scanln and fmt.Scan with the current 2016 go version (go version go1.6.2 linux/amd64).
N
Naren Yellavula

Always try to use the bufio.NewScanner for collecting input from the console. As others mentioned, there are multiple ways to do the job, but Scanner is originally intended to do the job. Dave Cheney explains why you should use Scanner instead of bufio.Reader's ReadLine.

https://twitter.com/davecheney/status/604837853344989184?lang=en

Here is the code snippet answer for your question

package main

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

/*
 Three ways of taking input
   1. fmt.Scanln(&input)
   2. reader.ReadString()
   3. scanner.Scan()

   Here we recommend using bufio.NewScanner
*/

func main() {
    // To create dynamic array
    arr := make([]string, 0)
    scanner := bufio.NewScanner(os.Stdin)
    for {
        fmt.Print("Enter Text: ")
        // Scans a line from Stdin(Console)
        scanner.Scan()
        // Holds the string that scanned
        text := scanner.Text()
        if len(text) != 0 {
            fmt.Println(text)
            arr = append(arr, text)
        } else {
            break
        }

    }
    // Use collected inputs
    fmt.Println(arr)
}

If you don't want to programmatically collect the inputs, just add these lines

   scanner := bufio.NewScanner(os.Stdin)
   scanner.Scan()
   text := scanner.Text()
   fmt.Println(text)

The output of above program will be:

Enter Text: Bob
Bob
Enter Text: Alice
Alice
Enter Text:
[Bob Alice]

The above program collects the user input and saves them to an array. We can also break that flow with a special character. Scanner provides API for advanced usage like splitting using a custom function, etc., scanning different types of I/O streams (standard Stdin, String), etc.

Edit: The tweet linked in original post is not accesible. But, one can find official reference of using Scanner from this standard library documentation: https://pkg.go.dev/bufio@go1.17.6#example-Scanner-Lines


This should be the accepted answer. Not only is it a more accurate answer but it's of better quality.
This is so much better than the other input methods.
so, you shall use scanner.Scan() prior to scanner.Text() that is mandatory in order to parse the input for stdin ending with an enter \n, right?
@Victor, yes. If you read through the official bufio docs, "Text returns the most recent token generated by a call to Scan as a newly allocated string holding its bytes." which exactly answers your question. golang.org/pkg/bufio/#Scanner.Text
Thanks @NarenYellavula. I do wonder why they provide both functions by seperated instead of aggregate then (in the right order) into a another abstraction, e.g.: ReadLine() (see this example: play.golang.org/p/cR8XjJ6WHtq). Another question regarding my thoughts is... what is the motivation of using Scan() without Text() or Bytes()? You see, a wise thing to do is if two things goes together (Scan + Text) why don't put then together into a single abstraction.
C
Chiheb Nexus

Another way to read multiple inputs within a loop which can handle an input with spaces:

package main
import (
    "fmt"
    "bufio"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    var text string
    for text != "q" {  // break the loop if text == "q"
        fmt.Print("Enter your text: ")
        scanner.Scan()
        text = scanner.Text()
        if text != "q" {
            fmt.Println("Your text was: ", text)
        }
    }
}

Output:

Enter your text: Hello world!
Your text was:  Hello world!
Enter your text: Go is awesome!
Your text was:  Go is awesome!
Enter your text: q

You could maybe just use a break in the inner "q" check and wrap it all in an infinite loop. Great answer by the way!
Looks like you can also get rid of the conditional in the for loop now too.
P
Peter Mortensen

It can also be done like this:

package main
import "fmt"

func main(){
    var myname string
    fmt.Scanf("%s", &myname)
    fmt.Println("Hello", myname)
}

S
Shivendra Mishra

I'm late to the party. But how about one liner:

data, err := ioutil.ReadAll(os.Stdin)

And press ctrl+d once done.


Because os.Stdin doesn't 'end' it's impossible to read it all. You might be waiting a while...
press ctrl+d i.e. eot.
Yes, that'd do it - reminds me of writing emails with mail.
This is deprecated from Go 1.16. Rather use io.ReadAll(os.Stdin)
P
Peter Mortensen

Cleanly read in a couple of prompted values:

// Create a single reader which can be called multiple times
reader := bufio.NewReader(os.Stdin)
// Prompt and read
fmt.Print("Enter text: ")
text, _ := reader.ReadString('\n')
fmt.Print("Enter More text: ")
text2, _ := reader.ReadString('\n')
// Trim whitespace and print
fmt.Printf("Text1: \"%s\", Text2: \"%s\"\n",
    strings.TrimSpace(text), strings.TrimSpace(text2))

Here's a run:

Enter text: Jim
Enter More text: Susie
Text1: "Jim", Text2: "Susie"

Also a nice way since strings.TrimSpace removes the '\n'. And I believe reader.ReadString('\n') is cross platform too.
I'll guess that most of the time you want to remove \n by default, that it's why it is better bufio.NewScanner as @Naren Yellavula answer
P
Peter Mortensen

Try this code:

var input string
func main() {
    fmt.Print("Enter Your Name=")
    fmt.Scanf("%s", &input)
    fmt.Println("Hello " + input)
}

Seems like Scanf() doesn't accept white spaces in string
P
Peter Mortensen

You need to provide a pointer to the variable you want to scan, like so:

fmt.scan(&text2)

P
Peter Mortensen

In my case, the program was not waiting because I was using the watcher command to auto run the program. Manually running the program go run main.go resulted in "Enter text" and eventually printing to the console.

fmt.Print("Enter text: ")
var input string
fmt.Scanln(&input)
fmt.Print(input)

The limitation of the Scan* family is that they read up to a whitespace (e.g. space) separator.
A
Arnaud Celermajer

Let's do it very simple

s:=""
b := make([]byte, 1)
for os.Stdin.Read(b) ; b[0]!='\n'; os.Stdin.Read(b) {
    s+=string(b)
    }
fmt.Println(s)

Isn't that super slow?!