【问题标题】:How can I read from standard input in the console?如何从控制台中的标准输入中读取?
【发布时间】:2014-01-20 15:28:43
【问题描述】:

我想从命令行读取标准输入,但我的尝试已经结束,程序在提示输入之前退出。我正在 C# 中寻找相当于 Console.ReadLine() 的内容。

这是我目前拥有的:

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)
}

【问题讨论】:

  • 这段代码看起来是正确的。出于好奇,你是在操场上运行这个吗?由于网络原因,Go Playground 不允许输入标准输入。
  • 没关系,这看起来是一个需要指针的微妙问题(请参阅我的回答)。虽然我不确定 bufio.NewReader 方法有什么问题,因为它对我有用。
  • 不要将任何阅读器的bufio缓冲(例如bufio.NewReader(os.Stdin))与来自下划线阅读器的直接读取(例如fmt.Scanln(x)直接从os.Stdin读取)混合。缓冲可以任意向前读取。 (在这种特定情况下,后者应该是 fmt.Fscanln(reader,x) 从同一个缓冲区读取)。
  • 我没有得到fmt.Sscanln的工作,运行后变成“%v”

标签: go


【解决方案1】:

我不确定该块有什么问题

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

因为它适用于我的机器。但是,对于下一个块,您需要一个指向要分配输入的变量的指针。尝试将fmt.Scanln(text2) 替换为fmt.Scanln(&text2)。不要使用Sscanln,因为它解析的是内存中已经存在的字符串,而不是来自标准输入的字符串。如果您想做类似您想做的事情,请将其替换为fmt.Scanf("%s", &ln)

如果这仍然不起作用,那么你的罪魁祸首可能是一些奇怪的系统设置或错误的 IDE。

【讨论】:

  • 那些应该是单引号吗? ReadString('\n')ReadString("\n")?
  • @425nesp 是的,那是分隔符,它是一个字节。 golang.org/pkg/bufio/#Reader.ReadString
  • 好答案,但是当我尝试使用退格键等键时失败
  • Golang 通过阅读器rd 从文件中读取一行到变量sif s,_ = rd.ReadString('\n'); true { s = strings.Trim(s, " \n") } 的内容太多了
  • 只是分享一个有趣的东西(我是Golang初学者): \n 必须在单引号内(不要尝试使用双引号)。否则,它会重现:cannot use "\n" (type string) as type byte in argument to reader.ReadString
【解决方案2】:

你也可以试试:

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

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

【讨论】:

  • 如果您只想要一个一行输入,您可以删除“for {}”。
  • 如果有 for {} 循环,当你进入时如何从循环中出来?是否有一个特殊字符会使 for 循环停止? - 谢谢
  • @Madhanscanner.Scan() 返回 bool 值以指示是否退出 for 循环。
  • 如果您的输入大于 64 * 1024 字节,您将收到此错误 bufio.Scanner: token too long。另外不要忘记在 for 循环下方添加 fmt.Println(scanner.Err())
  • 如果我输入“abc\n^D”,预期的字符串是“abc\n”,但它返回的是“abc”。
【解决方案3】:

我认为更标准的方法是:

package main

import "fmt"

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

看看scangodoc:http://godoc.org/fmt#Scan

Scan 扫描从标准输入读取的文本,将连续的以空格分隔的值存储到连续的参数中。换行符算作空格。

Scanln 类似于 Scan,但在换行处停止扫描,并且在最后一项之后必须有换行符或 EOF。

【讨论】:

  • 这似乎不喜欢输入字符串中的空格。
  • @HairyChris 是的,这很奇怪。在文档中它说stops scanning at a newline and after the final item there must be a newline or EOF 所以不确定为什么空间会“破坏”它......我想这是一个错误
  • 为此打开了一个错误:github.com/golang/go/issues/5703 它已作为 WorkingAsIntended 关闭。另请参阅:stackoverflow.com/questions/24005899/…groups.google.com/forum/#!topic/golang-nuts/r6Jl4D9Juw0 似乎很多人对此有疑问。需要更改文档吗?此外,从最后一个链接:“Scan 和 Scanln 用于解析和类似的东西,所以只从标准输入获取单行文本会破坏目的。”
  • 对我来说,fmt.Scan 的任何类似功能都不能很好地处理像 bufio.NewReader 这样的空格。
  • fmt.Scanlnfmt.Scan 与当前的2016 go 版本(go 版本go1.6.2 linux/amd64)一起使用时,同样的空格问题仍然存在。
【解决方案4】:

始终尝试使用 bufio.NewScanner 从控制台收集输入。正如其他人所提到的,有多种方法可以完成这项工作,但 Scanner 最初是为了完成这项工作。 Dave Cheney 解释了为什么应该使用 Scanner 而不是 bufio.Reader 的 ReadLine。

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

这是您问题的代码 sn-p 答案

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)
}

如果您不想以编程方式收集输入,只需添加这些行

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

上述程序的输出将是:

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

上面的程序收集用户输入并将它们保存到一个数组中。我们也可以用一个特殊的字符打破这个流程。 Scanner 提供 API 用于高级用法,例如使用自定义函数进行拆分等,扫描不同类型的 I/O 流(标准 StdinString)等。

编辑:原始帖子中链接的推文不可访问。但是,可以从这个标准库文档中找到使用 Scanner 的官方参考:https://pkg.go.dev/bufio@go1.17.6#example-Scanner-Lines

【讨论】:

  • 这应该是公认的答案。它不仅是一个更准确的答案,而且质量更好。
  • 这比其他输入法好太多了。
  • 所以,您应该在scanner.Text() 之前使用scanner.Scan(),这是为了解析以输入\n 结尾的stdin 输入,对吗?
  • @Victor,是的。如果您通读官方 bufio 文档,“文本返回由调用 Scan 生成的最新令牌作为新分配的保存其字节的字符串。”这正好回答了你的问题。 golang.org/pkg/bufio/#Scanner.Text
  • 谢谢@NarenYellavula。我确实想知道为什么他们通过分离而不是聚合然后(以正确的顺序)到另一个抽象来提供这两个函数,例如:ReadLine()(参见这个例子:play.golang.org/p/cR8XjJ6WHtq)。关于我的想法的另一个问题是......在没有 Text() 或 Bytes() 的情况下使用 Scan() 的动机是什么?你看,明智的做法是,如果两件事结合在一起(扫描 + 文本),为什么不把它们放在一起成为一个抽象。
【解决方案5】:

另一种在循环中读取多个输入的方法,可以处理带空格的输入:

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)
        }
    }
}

输出:

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

【讨论】:

  • 您可能只是在内部“q”检查中使用中断并将其全部包装在无限循环中。顺便说一句,答案很好!
  • 看起来你现在也可以摆脱 for 循环中的条件了。
【解决方案6】:

也可以这样:

package main
import "fmt"

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

【讨论】:

    【解决方案7】:

    我迟到了。但是一个班轮怎么样:

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

    完成后按 ctrl+d。

    【讨论】:

    • 因为os.Stdin 没有“结束”,所以不可能全部阅读。你可能要等一会儿……
    • 按 ctrl+d 即 eot。
    • 是的,这就够了 - 让我想起了用 mail 写电子邮件。
    • Go 1.16 已弃用。而是使用io.ReadAll(os.Stdin)
    【解决方案8】:

    清晰地读入几个提示值:

    // 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))
    

    这是一个运行:

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

    【讨论】:

    • 也是一个不错的方法,因为 strings.TrimSpace 删除了 '\n'。而且我相信 reader.ReadString('\n') 也是跨平台的。
    • 我猜大多数时候你想删除 \n 默认情况下,这就是为什么它更好 bufio.NewScanner 作为@Naren Yellavula 回答
    【解决方案9】:

    试试这个代码:

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

    【讨论】:

    • 好像Scanf() 不接受字符串中的空格
    【解决方案10】:

    您需要提供指向要扫描的变量的指针,如下所示:

    fmt.scan(&text2)
    

    【讨论】:

      【解决方案11】:

      就我而言,程序没有等待,因为我使用watcher 命令自动运行程序。手动运行程序 go run main.go 导致“输入文本”并最终打印到控制台。

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

      【讨论】:

      • Scan* 系列的限制是它们最多读取一个空格(例如空格)分隔符。
      【解决方案12】:

      让我们做的很简单

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-09-27
        • 2011-10-11
        • 1970-01-01
        • 1970-01-01
        • 2020-08-24
        • 2013-09-27
        • 2017-01-15
        相关资源
        最近更新 更多