英文:
multiline input in a terminal Go application
问题
我需要让用户在控制台输入多行文本。
以下是我的代码:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
for {
fmt.Println("如何在此处读取所有行?")
in := bufio.NewReader(os.Stdin)
result, err := in.ReadString('\n')
if err != nil {
fmt.Println(err)
}
fmt.Println("\nresult")
fmt.Println(result)
}
}
我在控制台中粘贴了:
Hello
World
它的输出是:
如何在此处读取所有行?
Hello
World
result
如何在此处读取所有行?
result
Hello
如何在此处读取所有行?
result
World
如何在此处读取所有行?
result
如何在此处读取所有行?
但我希望输出是:
如何在此处读取所有行?
Hello
World
result
如何在此处读取所有行?
result
Hello
World
如何在此处读取所有行?
我猜我需要使用`EOF`而不是`'\n'`。但具体如何操作呢?
# 更新
peterSo的答案有效,除非我尝试从剪贴板粘贴包含一个或多个空行的文本,例如:
Hello
World
它会打印:
输入行:
Hello
World结果:
Hello
输入行:
# 更新2
伟大的更新后的peterSO的答案现在可以处理包含空行的文本。
<details>
<summary>英文:</summary>
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?
#Update
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:
#Update 2
The great updated peterSO's answer now works even for text with empty lines.
</details>
# 答案1
**得分**: 8
缓冲一组行并检测一组行的结束。例如,
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scn := bufio.NewScanner(os.Stdin)
for {
fmt.Println("输入行:")
var lines []string
for scn.Scan() {
line := scn.Text()
if len(line) == 1 {
// 分组分隔符(GS ^]):ctrl-]
if line[0] == '\x1D' {
break
}
}
lines = append(lines, line)
}
if len(lines) > 0 {
fmt.Println()
fmt.Println("结果:")
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
}
}
}
控制台:
<pre>
输入行:
Hello
World
^]
结果:
Hello
World
输入行:
Farewell
World
^]
结果:
Farewell
World
输入行:
^]
</pre>
要终止一组行,请在空行上输入:`ctrl+`][Enter]。要终止输入,请输入一行:`ctrl+`][Enter]。
<details>
<summary>英文:</summary>
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:
<pre>
Enter Lines:
Hello
World
^]
Result:
Hello
World
Enter Lines:
Farewell
World
^]
Result:
Farewell
World
Enter Lines:
^]
</pre>
To terminate a set of lines, on an empty line, enter: <`ctrl+`]><`Enter`>. To terminate input, enter a single line: <`ctrl+`]><`Enter`>.
</details>
# 答案2
**得分**: 1
请查看[bufio.ReadString][1]的文档,它指出你传递给它的字符将导致函数停止读取并返回。如果你想接受多行输入,你需要想出一些方案来区分继续语句的换行符('\n')和终止语句的字符。
**编辑:**下面是一个简单的示例,通过扩展你的代码来识别多行输入。它通过将语句的“终止”定义为空字符串(只包含“\n”)来实现。你可以看到它只是将多个读取缓冲到一个“块”中。你可以更聪明地直接读取键盘,但无论如何,你都必须定义一些方案,以便在程序中可以识别多行和单行。
```go
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
}
}
}
英文:
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
}
}
}
答案3
得分: 0
EOF
无法帮助你,因为io.EOF
的类型是'error
',但in.ReadString
期望的是一个字节(序列'\n
'是一个字节)。但无论如何,如果我理解你的目标,你需要一种停止从stdin读取的方法。例如,一个特定的字符串。就像mysql客户端期望"exit;"字符串来结束你的会话。
看一下扫描行的示例http://golang.org/pkg/bufio/#example_Scanner_lines,并改进它,将
fmt.Println(scanner.Text()) // Println will add back the final '\n'
替换为类似以下的内容:
allInput += scanner.Text() + "\n"
if scanner.Text() == "exit;" {
//退出时的操作...
//...
fmt.Println("Exiting. Bye")
return
}
或者,如果你希望你的程序在管道中工作,并从其他程序的输出中接收多行输入,你可以使用io.ReadFull
或PipeReader
函数。
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论