Reputation: 14165
I need to let a user input multiline text to the console.
Here is my code:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
for {
fmt.Println("How to read all lines here?")
in := bufio.NewReader(os.Stdin)
result, err := in.ReadString('\n')
if err != nil {
fmt.Println(err)
}
fmt.Println("\nresult")
fmt.Println(result)
}
}
I pasted in the console:
Hello
World
It outputs:
How to read all lines here?
Hello
World
result
How to read all lines here?
result
Hello
How to read all lines here?
result
World
How to read all lines here?
result
How to read all lines here?
But I expect it to be:
How to read all lines here?
Hello
World
result
How to read all lines here?
result
Hello
World
How to read all lines here?
I guess I need to use something like EOF
instead of '\n'
But how to do it exactly?
peterSo's answer works except in case when I am trying to paste from clipboard a text with one or more empty lines in-between, like:
Hello
World
It prints
Enter Lines:
Hello
WorldResult:
Hello
Enter Lines:
The great updated peterSO's answer now works even for text with empty lines.
Upvotes: 3
Views: 7486
Reputation: 166569
Buffer a set of lines and detect the end of a set of lines. For example,
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scn := bufio.NewScanner(os.Stdin)
for {
fmt.Println("Enter Lines:")
var lines []string
for scn.Scan() {
line := scn.Text()
if len(line) == 1 {
// Group Separator (GS ^]): ctrl-]
if line[0] == '\x1D' {
break
}
}
lines = append(lines, line)
}
if len(lines) > 0 {
fmt.Println()
fmt.Println("Result:")
for _, line := range lines {
fmt.Println(line)
}
fmt.Println()
}
if err := scn.Err(); err != nil {
fmt.Fprintln(os.Stderr, err)
break
}
if len(lines) == 0 {
break
}
}
}
Console:
Enter Lines: Hello World ^] Result: Hello World Enter Lines: Farewell World ^] Result: Farewell World Enter Lines: ^]
To terminate a set of lines, on an empty line, enter: <ctrl+
]><Enter
>. To terminate input, enter a single line: <ctrl+
]><Enter
>.
Upvotes: 8
Reputation: 176
EOF
can not help you, because io.EOF
has type of 'error
', but in.ReadString
expects a byte (the sequence '\n'
- is a byte). But anyway, if I understand your goals, you need some way to stop reading from stdin. For example, a specific string. Like mysql client expects "exit;" string to end your session.
Take a look at scanning lines example http://golang.org/pkg/bufio/#example_Scanner_lines and improve it replacing
fmt.Println(scanner.Text()) // Println will add back the final '\n'
with something like:
allInput += scanner.Text() + "\n"
if scanner.Text() == "exit;" {
//your actions on exit...
//...
fmt.Println("Exiting. By")
return
}
Or if you want you program to work in pipes and take multiline input from other program output, you can use io.ReadFull
or PipeReader
functions
Upvotes: 0
Reputation: 427
Look at the documentation for bufio.ReadString, it states that the character you pass to it will cause the function to stop reading and return. If you want to accept multi-line input, you'll need to come up with some scheme to differentiate newlines ('\n') that continue a statement, and whatever terminates a statement.
edit: Here is a bad quick hacky example that extends your code to recognize multi-line input. It does this by defining the "termination" of a statement to be the empty string containing only "\n". You can see it's just buffering multiple reads into one "block". You can get more clever and read the keyboard directly, but regardless you'll have to define some scheme that you can programmatically recognize as being multi-line vs. single line.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
cnt := false
var txt []byte
for {
result := make([]byte, 1024)
if cnt == false {
fmt.Println("How to read all lines here?")
}
in := bufio.NewReader(os.Stdin)
_, err := in.Read(result)
if err != nil {
fmt.Println(err)
}
if result[0] == '\n' {
cnt = false
fmt.Printf("<----\n%s---->\n", string(txt))
txt = txt[:0]
} else {
txt = append(txt, result...)
cnt = true
}
}
}
Upvotes: 1