ChatGPT解决这个技术问题 Extra ChatGPT

如何从控制台中的标准输入中读取?

go

我想从命令行读取标准输入,但我的尝试已经结束,程序在提示输入之前退出。我在 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 方法的问题是什么,因为它对我有用。
Read from initial stdin in GO? 的可能重复项
不要将任何读取器的 bufio 缓冲(例如 bufio.NewReader(os.Stdin))与来自下划线读取器的直接读取(例如 fmt.Scanln(x) 直接从 os.Stdin 读取)混合。缓冲可以任意向前读取。 (在这种特定情况下,后者应该是 fmt.Fscanln(reader,x) 从同一个缓冲区读取)。
我没有得到 fmt.Sscanln 作品,运行后它变成“%v”

L
Linear

我不确定该块有什么问题

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 从文件中读取一行到变量 s 作为 if 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
P
Peter Mortensen

你也可以试试:

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())
我们不能增加限制。它被硬编码到 go 运行时中。
C
Community

我认为更标准的方法是:

package main

import "fmt"

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

查看 scan godoc: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 用于解析和类似的东西,因此仅从 stdin 获取单行文本会破坏目的。”
对我来说,fmt.Scan 的任何类似功能都不能很好地与 bufio.NewReader 这样的空间配合使用,这真的很令人困惑。
在当前 2016 go 版本(go 版本 go1.6.2 linux/amd64)中使用 fmt.Scanlnfmt.Scan 时,同样的空格问题仍然存在。
N
Naren Yellavula

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

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

这是您问题的代码片段答案

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(),这是必需的,以便解析以 enter \n 结尾的标准输入的输入,对吧?
@Victor,是的。如果您通读官方 bufio 文档,“文本返回由调用 Scan 生成的最新令牌,作为新分配的保存其字节的字符串。”这正好回答了你的问题。 golang.org/pkg/bufio/#Scanner.Text
谢谢@NarenYellavula。我确实想知道为什么他们通过分离而不是聚合然后(以正确的顺序)到另一个抽象来提供这两个函数,例如:ReadLine()(参见这个例子:play.golang.org/p/cR8XjJ6WHtq)。关于我的想法的另一个问题是......在没有 Text() 或 Bytes() 的情况下使用 Scan() 的动机是什么?您会看到,明智的做法是,如果两件事结合在一起(扫描 + 文本),为什么不将它们放在一起成为一个抽象。
C
Chiheb Nexus

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

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 循环中的条件。
P
Peter Mortensen

也可以这样做:

package main
import "fmt"

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

S
Shivendra Mishra

我参加晚会迟到了。但是一个班轮怎么样:

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

完成后按 ctrl+d。


因为 os.Stdin 没有“结束”,所以不可能全部阅读。你可能要等一段时间...
按 ctrl+d 即 eot。
是的,这就够了 - 让我想起了用 mail 写电子邮件。
这在 Go 1.16 中已被弃用。而是使用 io.ReadAll(os.Stdin)
P
Peter Mortensen

清楚地读入几个提示值:

// 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 答案
P
Peter Mortensen

试试这个代码:

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

似乎 Scanf() 不接受字符串中的空格
P
Peter Mortensen

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

fmt.scan(&text2)

P
Peter Mortensen

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

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

Scan* 系列的限制是它们最多读取一个空格(例如空格)分隔符。
A
Arnaud Celermajer

让我们做起来很简单

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

是不是超级慢?!