在终端中的 Go 应用程序中使用多行输入。

huangapple go评论120阅读模式
英文:

multiline input in a terminal Go application

问题

我需要让用户在控制台输入多行文本。

以下是我的代码:

  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. )
  7. func main() {
  8. for {
  9. fmt.Println("如何在此处读取所有行?")
  10. in := bufio.NewReader(os.Stdin)
  11. result, err := in.ReadString('\n')
  12. if err != nil {
  13. fmt.Println(err)
  14. }
  15. fmt.Println("\nresult")
  16. fmt.Println(result)
  17. }
  18. }

我在控制台中粘贴了:

  1. Hello
  2. World

它的输出是:

  1. 如何在此处读取所有行?
  2. Hello
  3. World
  4. result
  5. 如何在此处读取所有行?
  6. result
  7. Hello
  8. 如何在此处读取所有行?
  9. result
  10. World
  11. 如何在此处读取所有行?
  12. result
  13. 如何在此处读取所有行?
  14. 但我希望输出是:

如何在此处读取所有行?

  1. Hello
  2. World

result

如何在此处读取所有行?

result
Hello
World

如何在此处读取所有行?

  1. 我猜我需要使用`EOF`而不是`'\n'`。但具体如何操作呢?
  2. # 更新
  3. peterSo的答案有效,除非我尝试从剪贴板粘贴包含一个或多个空行的文本,例如:

Hello

World

  1. 它会打印:

输入行:
Hello

World结果:
Hello

输入行:

  1. # 更新2
  2. 伟大的更新后的peterSO的答案现在可以处理包含空行的文本。
  3. <details>
  4. <summary>英文:</summary>
  5. I need to let a user input multiline text to the console.
  6. Here is my code:
  7. package main
  8. import (
  9. &quot;bufio&quot;
  10. &quot;fmt&quot;
  11. &quot;os&quot;
  12. )
  13. func main() {
  14. for {
  15. fmt.Println(&quot;How to read all lines here?&quot;)
  16. in := bufio.NewReader(os.Stdin)
  17. result, err := in.ReadString(&#39;\n&#39;)
  18. if err != nil {
  19. fmt.Println(err)
  20. }
  21. fmt.Println(&quot;\nresult&quot;)
  22. fmt.Println(result)
  23. }
  24. }
  25. I pasted in the console:
  26. Hello
  27. World
  28. It outputs:
  29. How to read all lines here?
  30. Hello
  31. World
  32. result
  33. How to read all lines here?
  34. result
  35. Hello
  36. How to read all lines here?
  37. result
  38. World
  39. How to read all lines here?
  40. result
  41. How to read all lines here?
  42. But I expect it to be:
  43. How to read all lines here?
  44. Hello
  45. World
  46. result
  47. How to read all lines here?
  48. result
  49. Hello
  50. World
  51. How to read all lines here?
  52. I guess I need to use something like `EOF` instead of `&#39;\n&#39;`
  53. But how to do it exactly?
  54. #Update
  55. peterSo&#39;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:
  56. Hello
  57. World
  58. It prints
  59. Enter Lines:
  60. Hello
  61. WorldResult:
  62. Hello
  63. Enter Lines:
  64. #Update 2
  65. The great updated peterSO&#39;s answer now works even for text with empty lines.
  66. </details>
  67. # 答案1
  68. **得分**: 8
  69. 缓冲一组行并检测一组行的结束。例如,
  70. package main
  71. import (
  72. "bufio"
  73. "fmt"
  74. "os"
  75. )
  76. func main() {
  77. scn := bufio.NewScanner(os.Stdin)
  78. for {
  79. fmt.Println("输入行:")
  80. var lines []string
  81. for scn.Scan() {
  82. line := scn.Text()
  83. if len(line) == 1 {
  84. // 分组分隔符(GS ^]):ctrl-]
  85. if line[0] == '\x1D' {
  86. break
  87. }
  88. }
  89. lines = append(lines, line)
  90. }
  91. if len(lines) > 0 {
  92. fmt.Println()
  93. fmt.Println("结果:")
  94. for _, line := range lines {
  95. fmt.Println(line)
  96. }
  97. fmt.Println()
  98. }
  99. if err := scn.Err(); err != nil {
  100. fmt.Fprintln(os.Stderr, err)
  101. break
  102. }
  103. if len(lines) == 0 {
  104. break
  105. }
  106. }
  107. }
  108. 控制台:
  109. <pre>
  110. 输入行:
  111. Hello
  112. World
  113. ^]
  114. 结果:
  115. Hello
  116. World
  117. 输入行:
  118. Farewell
  119. World
  120. ^]
  121. 结果:
  122. Farewell
  123. World
  124. 输入行:
  125. ^]
  126. </pre>
  127. 要终止一组行,请在空行上输入:`ctrl+`][Enter]。要终止输入,请输入一行:`ctrl+`][Enter]。
  128. <details>
  129. <summary>英文:</summary>
  130. Buffer a set of lines and detect the end of a set of lines. For example,
  131. package main
  132. import (
  133. &quot;bufio&quot;
  134. &quot;fmt&quot;
  135. &quot;os&quot;
  136. )
  137. func main() {
  138. scn := bufio.NewScanner(os.Stdin)
  139. for {
  140. fmt.Println(&quot;Enter Lines:&quot;)
  141. var lines []string
  142. for scn.Scan() {
  143. line := scn.Text()
  144. if len(line) == 1 {
  145. // Group Separator (GS ^]): ctrl-]
  146. if line[0] == &#39;\x1D&#39; {
  147. break
  148. }
  149. }
  150. lines = append(lines, line)
  151. }
  152. if len(lines) &gt; 0 {
  153. fmt.Println()
  154. fmt.Println(&quot;Result:&quot;)
  155. for _, line := range lines {
  156. fmt.Println(line)
  157. }
  158. fmt.Println()
  159. }
  160. if err := scn.Err(); err != nil {
  161. fmt.Fprintln(os.Stderr, err)
  162. break
  163. }
  164. if len(lines) == 0 {
  165. break
  166. }
  167. }
  168. }
  169. Console:
  170. &lt;pre&gt;
  171. Enter Lines:
  172. Hello
  173. World
  174. ^]
  175. Result:
  176. Hello
  177. World
  178. Enter Lines:
  179. Farewell
  180. World
  181. ^]
  182. Result:
  183. Farewell
  184. World
  185. Enter Lines:
  186. ^]
  187. &lt;/pre&gt;
  188. To terminate a set of lines, on an empty line, enter: &lt;`ctrl+`]&gt;&lt;`Enter`&gt;. To terminate input, enter a single line: &lt;`ctrl+`]&gt;&lt;`Enter`&gt;.
  189. </details>
  190. # 答案2
  191. **得分**: 1
  192. 请查看[bufio.ReadString][1]的文档,它指出你传递给它的字符将导致函数停止读取并返回。如果你想接受多行输入,你需要想出一些方案来区分继续语句的换行符('\n')和终止语句的字符。
  193. **编辑:**下面是一个简单的示例,通过扩展你的代码来识别多行输入。它通过将语句的“终止”定义为空字符串(只包含“\n”)来实现。你可以看到它只是将多个读取缓冲到一个“块”中。你可以更聪明地直接读取键盘,但无论如何,你都必须定义一些方案,以便在程序中可以识别多行和单行。
  194. ```go
  195. package main
  196. import (
  197. "bufio"
  198. "fmt"
  199. "os"
  200. )
  201. func main() {
  202. cnt := false
  203. var txt []byte
  204. for {
  205. result := make([]byte, 1024)
  206. if cnt == false {
  207. fmt.Println("How to read all lines here?")
  208. }
  209. in := bufio.NewReader(os.Stdin)
  210. _, err := in.Read(result)
  211. if err != nil {
  212. fmt.Println(err)
  213. }
  214. if result[0] == '\n' {
  215. cnt = false
  216. fmt.Printf("<----\n%s---->\n", string(txt))
  217. txt = txt[:0]
  218. } else {
  219. txt = append(txt, result...)
  220. cnt = true
  221. }
  222. }
  223. }
英文:

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.

  1. package main
  2. import (
  3. &quot;bufio&quot;
  4. &quot;fmt&quot;
  5. &quot;os&quot;
  6. )
  7. func main() {
  8. cnt := false
  9. var txt []byte
  10. for {
  11. result := make([]byte, 1024)
  12. if cnt == false {
  13. fmt.Println(&quot;How to read all lines here?&quot;)
  14. }
  15. in := bufio.NewReader(os.Stdin)
  16. _, err := in.Read(result)
  17. if err != nil {
  18. fmt.Println(err)
  19. }
  20. if result[0] == &#39;\n&#39; {
  21. cnt = false
  22. fmt.Printf(&quot;&lt;----\n%s----&gt;\n&quot;, string(txt))
  23. txt = txt[:0]
  24. } else {
  25. txt = append(txt, result...)
  26. cnt = true
  27. }
  28. }
  29. }

答案3

得分: 0

EOF无法帮助你,因为io.EOF的类型是'error',但in.ReadString期望的是一个字节(序列'\n'是一个字节)。但无论如何,如果我理解你的目标,你需要一种停止从stdin读取的方法。例如,一个特定的字符串。就像mysql客户端期望"exit;"字符串来结束你的会话。

看一下扫描行的示例http://golang.org/pkg/bufio/#example_Scanner_lines,并改进它,将

  1. fmt.Println(scanner.Text()) // Println will add back the final '\n'

替换为类似以下的内容:

  1. allInput += scanner.Text() + "\n"
  2. if scanner.Text() == "exit;" {
  3. //退出时的操作...
  4. //...
  5. fmt.Println("Exiting. Bye")
  6. return
  7. }

或者,如果你希望你的程序在管道中工作,并从其他程序的输出中接收多行输入,你可以使用io.ReadFullPipeReader函数。

英文:

EOF can not help you, because io.EOF has type of 'error', but in.ReadString expects a byte (the sequence &#39;\n&#39; - 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

  1. fmt.Println(scanner.Text()) // Println will add back the final &#39;\n&#39;

with something like:

  1. allInput += scanner.Text() + &quot;\n&quot;
  2. if scanner.Text() == &quot;exit;&quot; {
  3. //your actions on exit...
  4. //...
  5. fmt.Println(&quot;Exiting. By&quot;)
  6. return
  7. }

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

huangapple
  • 本文由 发表于 2015年6月14日 12:40:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/30825996.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定