最近刷题的时候,发现大家都喜欢用 bufio.Scanner 或 bufio.Reader 来从标准输入获取数据,遂来了解一下它们的特性。
文中为了调试方便,使用的 strings.NewReader("input string") 暂时替换 os.Stdin,实际从标准输入获取时可直接使用 os.Stdin 进行替换。
bufio.Scanner
简单来讲,bufio.Scanner 是 Go 中一个用于逐个读取输入缓冲区的扫描器,通常与 bufio.Reader 一起使用,bufio.Reader 用于从输入中读取数据,而 bufio.Scanner 则用于逐个读取输入缓冲区的内容。
bufio.Scanner 可以将输入数据分解为逻辑上的行并返回。Scanner 通过定义一个 Split 函数来将输入分解为行,默认是使用的 bufio.ScanLines,也即默认一次 Scan() 操作读取一行数据。
下面是 bufio.Scanner 提供的一些主要方法:
- func (s *Scanner) Scan() bool,用于读取输入缓冲区中的下一个数据块,并将其保存在内部的缓冲区中。如果读取成功,则返回 true;如果已经读取了所有数据或者发生了错误,则返回 false。
- func (s *Scanner) Text() string,用于获取内部缓冲区中的文本内容,通常与 Scan() 方法一起使用,用于获取读取的数据。
- func (s *Scanner) Bytes() []byte,用于获取内部缓冲区中的字节内容,通常与 Scan() 方法一起使用,用于获取读取的数据。
- func (s *Scanner) Err() error,用于获取在读取输入时发生的错误信息,如果读取过程中没有发生错误,则返回 nil;否则,返回一个非 nil 的错误对象。
- func (s *Scanner) Buffer(buf []byte, max int), 用于自定义输入缓冲区大小,接受一个 []byte 类型的参数,用于指定缓冲区的大小。
- func (s *Scanner) Split(split SplitFunc),用于指定一个分割函数,将输入分割成多个数据块,接受一个 func([]byte) bool 类型的参数,该函数在每次读取输入时被调用,用于判断是否需要将当前数据块分割成多个小块。通常用于处理非常大的数据块,以避免内存溢出等问题。
bufio.ScanLines
这个也是用作默认的分隔函数,默认每次 Scan() 读取一行。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// input := os.Stdin
input := strings.NewReader("hello world\nsecond line\nend line")
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanLines) // 如果保持默认可不写这一行
for scanner.Scan() {
fmt.Printf("%#v\n", scanner.Text())
}
}
"hello world"
"second line"
"end line
bufio.ScanWords
简单理解,就是每次 Scan() 按照单词进行扫描读取。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// input := os.Stdin
input := strings.NewReader("hello world\nsecond line\nend line")
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Printf("%#v\n", scanner.Text())
}
}
"hello"
"world"
"second"
"line"
"end"
"line"
bufio.ScanRunes
简单理解,就是每次 Scan() 按照一个字符进行扫描读取(包括汉字等多字节字符)。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// input := os.Stdin
input := strings.NewReader("hello好world")
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanRunes)
for scanner.Scan() {
fmt.Printf("%#v\n", scanner.Text())
}
}
"h"
"e"
"l"
"l"
"o"
"好"
"w"
"o"
"r"
"l"
"d"
bufio.ScanBytes
简单理解,就是每次 Scan() 按照一个字节进行扫描读取(特殊字符如汉字等多字节字符会被分成多次才能读取完成)。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// input := os.Stdin
input := strings.NewReader("hello好world")
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanBytes)
for scanner.Scan() {
fmt.Printf("%#v\n", scanner.Text())
}
}
"h"
"e"
"l"
"l"
"o"
"\xe5"
"\xa5"
"\xbd"
"w"
"o"
"r"
"l"
"d"
自定义分隔函数
我们也可以自定义分隔函数,比如说使用逗号进行分隔。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// input := os.Stdin
input := strings.NewReader("hello,world")
scanner := bufio.NewScanner(input)
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
// 分隔符为逗号
for i, d := range data {
if d == ',' {
return i + 1, data[:i], nil
}
}
if atEOF && len(data) > 0 {
return len(data), data, nil
}
return 0, nil, nil
})
for scanner.Scan() {
fmt.Printf("%#v\n", scanner.Text())
}
}
"hello"
"world"
bufio.Reader
reader.ReadLine()
按行读取,但是记得读取的行不包括换行符本身哟。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// read := os.Stdin
read := strings.NewReader("hello world\nsecond line\nend line")
reader := bufio.NewReader(read)
for {
s, _, _ := reader.ReadLine()
if s == nil {
break
}
fmt.Printf("%#v\n", string(s))
}
}
reader.ReadString()
ReadString() 需要自己指定读取的分隔符,比如 \n ,且读取的结果会包含分隔符本身,所以一般按照 \n 读取以后,需要使用 strings.TrimSpace()去掉字符串两端的空白。
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// read := os.Stdin
read := strings.NewReader("hello world\n\rsecond line\nend line")
reader := bufio.NewReader(read)
for {
s, _ := reader.ReadString('\n')
if s == "" {
break
}
fmt.Printf("%#v\n", s)
// fmt.Printf("%#v\n", strings.TrimSpace(s))
}
}
"hello world\n"
"\rsecond line\n"
"end line"
reader.ReadRune()
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// read := os.Stdin
read := strings.NewReader("hello好world")
reader := bufio.NewReader(read)
for {
s, _, _ := reader.ReadRune()
if s == 0 {
break
}
fmt.Printf("%#v\n", string(s))
}
}
"h"
"e"
"l"
"l"
"o"
"好"
"w"
"o"
"r"
"l"
"d"
reader.ReadByte()
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// read := os.Stdin
read := strings.NewReader("hello好world")
reader := bufio.NewReader(read)
for {
s, _ := reader.ReadByte()
if s == 0 {
break
}
fmt.Printf("%#v\n", s)
}
}
0x68
0x65
0x6c
0x6c
0x6f
0xe5
0xa5
0xbd
0x77
0x6f
0x72
0x6c
0x64
fmt.Scan
fmt.Scan
读取由空白符分隔的值保存到传递给参数,换行符视为空白符。
package main
import (
"fmt"
)
func main() {
var (
name string
age int
married bool
)
fmt.Scan(&name, &age, &married) // 读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
// 输入:Looking 29 true
// 输出:扫描结果 name:Looking age:29 married:true
}
fmt.Scanf
根据 format 参数指定的格式去读取值保存传递给参数。文章来源:https://www.toymoban.com/news/detail-852644.html
package main
import (
"fmt"
)
func main() {
var (
name string
age int
married bool
)
fmt.Scanf("1:%s 2:%d 3:%t", &name, &age, &married) // 根据format参数指定的格式去读取值保存到传递给本函数的参数中。
fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
// 输入:1:Looking 2:29 3:true
// 输出:扫描结果 name:Looking age:29 married:true
}
fmt.Scanln
Scanln 类似 Scan,它在遇到换行时才停止扫描。文章来源地址https://www.toymoban.com/news/detail-852644.html
package main
import (
"fmt"
)
func main() {
var (
name string
age int
married bool
)
fmt.Scanln(&name, &age, &married) // Scanln类似Scan,它在遇到换行时才停止扫描。
fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
// 输入:Looking 29 true
// 输出:扫描结果 name:Looking age:29 married:true
}
到了这里,关于Go 之从标准输入读取数据的方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!