【备忘】Go语言标准输入的基础知识

将以下内容作为我个人备忘录,关于Go语言的标准输入。
这是一般常见的内容,无处不在。

<参考网站>
· 【Golang】fmt.Scan和bufio.Scanner的速度比较
· Go语言竞技编程的标准输入读取方法 — 第二版修订

使用fmt库进行标准输入。

首先,可以使用 fmt.Scan 方法进行读取。
这种方法很简单易用,但在读取大量输入时可能会出现速度较慢等缺点。

1-1. 基本的样本 de

通过以下方式,我们可以获取从控制台输入的内容:fmt.Scan(&存储位置的变量)。
& 表示变量的地址。

package main

import "fmt"

func main() {
    var str string
    fmt.Scan(&str) // データを格納する変数のアドレスを指定
    fmt.Println(str)
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
123 456 789
123

如果使用fmt.Scan,它会将换行符或空格作为一个完整的输入读取。在上面的例子中,输入的内容是123 456 789,但只有前面的空格分隔的123被获取到。

1-2. 如果要读取以空格分隔的输入的情况

要读取以空格分隔的输入,只需简单地进行所需次数的读取。
您可以按照以下方式,在fmt.Scan的参数中通过逗号分隔指定变量,以接收多个输入。

package main

import "fmt"

func main() {
    var n [3]int
    fmt.Scan(&n[0], &n[1], &n[2])
    fmt.Println(n)
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
123 456 789
[123 456 789]

假设上述示例中有三个用空格分隔的值,但无法处理值的数量未知的情况(如果数量未知,请使用下一节的bufio)。

在这里,我们使用 int 类型的变量来接收输入。
在 fmt.Scan 中,您可以直接接收所需的数据类型的输入值,无需后续进行数据类型转换。

只需根据需要接受输入的次数,您可以使用以下方式来获得相同的结果。即使是接受多行输入的情况,也可以使用此方法来处理。

func main() {
    var n [3]int
    for i := 0; i < 3; i++ {
        fmt.Scan(&n[i])
    }
    fmt.Println(n)
}

使用 bufio 库进行标准输入

就个人来说,我觉得这个 bufio 库更加方便使用。
不过,因为需要以字符串类型(string)获取输入,所以会涉及到更改数据类型等一些额外的工作。

2-1. 基本的样品

首先,使用bufio.NewScanner生成一个接受标准输入(os.Stdin)的扫描器。
然后,使用scanner.Scan()进行一行的扫描,并使用scanner.Text()获取输入值(scanner只是一个变量,可以用s或其他任何名称代替)。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin) // 標準入力を受け付けるスキャナ
    scanner.Scan() // 1行分の入力を取得する
    fmt.Println(scanner.Text())
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
abc 123 456
abc 123 456

如果使用bufio,它会默认将“换行”作为输入的分隔符。就像上面所述,即使输入包含空格,例如”abc 123 456″,仍然会将其作为一行输入获取。

加载超过默认大小(65,536个字符)的内容。

在bufio包中,默认情况下,读取的最大字符数被设置为MaxScanTokenSize(65536个字符)。
如果想要扫描更多的字符数,可以使用Buffer函数(方法)如下所示。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Buffer(make([]byte, 64*1024), 100001) // 最大100,001文字
    scanner.Scan()
    fmt.Println(scanner.Text())
}

如果将上述示例中的最大读取字符数指定为100,001个字符。

缓冲区函数用以下语法表示。

func (s *Scanner) Buffer(buf []byte, max int)

buf用于指定在扫描时使用的初始缓冲区(代码中对应64 * 1024)。
max用于指定在扫描过程中可能分配的缓冲区的最大大小(代码中对应100001)。

获取以空格分隔的输入,并分别处理。

如果逐行获取输入,则可以通过以空格分隔的方式进行分割,以获取每个值。在分割过程中使用 strings.Split(目标字符串, 分隔符)。

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    inputs := strings.Split(scanner.Text(), " ")
    for _, input := range inputs {
        fmt.Println(input)
    }
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
aaa bbb ccc ddd eee
aaa
bbb
ccc
ddd
eee

上面的例子中,我们获取了一行输入 aaa bbb ccc ddd eee,然后进行以空格为间隔的分解。
即使空格分隔的数量是未知的,也可以进行相应的处理。

获取多行输入

要获取多个输入,需要重复执行scanner.Scan()的次数(scanner只是一个变量,可以用s或其他任何变量名代替)。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    var n int = 3
    var str []string
    scanner := bufio.NewScanner(os.Stdin)
    for i := 0; i < n; i++ {
        scanner.Scan()
        str = append(str, scanner.Text())
    }
    fmt.Println(str)
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
123
456
789
[123 456 789]

读取文本文件

最后,我们也留一种方法来读取文本文件。
作为准备,我们将文本文件保存在与 main.go 相同的文件夹内。

abcdefghijklmnopqrstuvwxyz
あいうえおかきくけこさしすせそ

3-1. 基本的示例

首先,假设没有发生错误,我会简单地描述一下。

可以通过使用bufio.NewScanner来获取文本文件的内容,如下所示。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, _ := os.Open("test.txt") // ファイルを開く
    scanner := bufio.NewScanner(file) // ファイルを読み取るスキャナー
    for scanner.Scan() { // ファイルを1行ずつ読み込む(読み込む行が無かった場合はFalse)
        fmt.Println(scanner.Text())
    }
}
C:\golang\sample>go build main.go
C:\golang\sample>.\main
abcdefghijklmnopqrstuvwxyz
あいうえおかきくけこさしすせそ

3-2. 包含错误处理的示例。

包括错误处理在内,如下所示。

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    file, err := os.Open(`C:\golang\sample\test.txt`)
    if err != nil { // ファイルを開く際にエラーが生じた場合
        fmt.Println(err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
    if scanner.Err() != nil { // 読み込み時にエラーがあった場合
        fmt.Println(scanner.Err())
    }
}
bannerAds