文章摘要:go语言中使用scanner去标准输入中扫描数据的过程以及原理
1、Scanner的简单使用
会默认以换行符作为结束标准
//创建一个新的Scanner,它将从标准输入读取数据
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
fmt.Println("输入的文本是:", scanner.Text())
}
2、Scanner读取文件
func main() {
file, err := os.Open("example.txt")
if err != nil {
fmt.Println("打开文件失败:", err)
return
}
defer file.Close()
//bufio.NewScanner中传参io.Reader 接口的类型,为了可以从输入流中读取数据的
scanner := bufio.NewScanner(file)
//读取到的数据会以字节切片([]byte)的形式存储在内部缓冲区s.buf中。
//之后可以通过scanner.Bytes()方法获取缓冲区中的数据。或者Text将其转换为字符串格式
for scanner.Scan() {
fmt.Println(scanner.Text()) // 打印每一行
}
}
3、Scanner通过io.Reader读取数据的过程
用到的方法就是scanner.Scan()
1、详细看Scan()内部
首先看Scan是一个方法,调用它的接口类型是Scanner,下面是这个对象的三个变量
- r:是io.Reader接口类型的对象,代表输入数据的源头。可以是文件、网络连接、标准输入等任何实现了
io.Reader
接口的数据源 - split:是 一个可选的分割函数,用于定义如何从输入数据中提取 token(分割的标识)
- maxTokenSize:是指定了单个 token 的最大长度,默认是64k,超过会报错!
func NewScanner(r io.Reader) *Scanner {
return &Scanner{
r: r,
split: ScanLines,
maxTokenSize: MaxScanTokenSize,
}
}
从输入源(io.Reader)中读取到的数据会以字节的形式存储到内部缓冲区s.buf,之后就可以根据split,找到数据中的token(分割的标识)进行分割。
2、详细看scan扫描分割符(token)原理
比如说 “世 界” 这个字符串,采用让他用空格作为分隔符将两个字打印 世界转换为字节码就是[228 184 150 32 231 149 140],一个汉字三个字节,最中间的32,就是空格符的Ascall码,当scan()在扫描 内部缓冲区s.buf 扫到32,然后就进行分割
func main() {
// 将字符串转换成字节数组
str := "世 界"
byteArr := []byte(str)
// 输出字节数组
fmt.Println(byteArr) //[228 184 150 32 231 149 140]
// 将字符串转换为 io.Reader 类型
reader := bytes.NewReader([]byte(str))
// 创建 Scanner 对象并指定分割函数为空格
scanner := bufio.NewScanner(reader)
scanner.Split(bufio.ScanWords)//bufio.ScanWords指的就是token为按照单词为单位进行分割
// 遍历 Scanner 输出每个 token
for scanner.Scan() {
fmt.Println(scanner.Text())
}
//世
//界
}
- 空格(space) ASCII 码值: 32
- 制表符(Tab) ASCII 码值: 9
- 换行符(Newline) ASCII 码值: 10 (LF, Line Feed)
- 回车符(carriage Return) ASCII 码值: 13 (CR)
- EOF(End of File) ASCII 码值: 4
当然记这些ASCLL码符号还不够麻烦呢,Go语言有内置的几个预定义函数,可以实现简单的分割类型
3、详细看scanner内置的Split分割函数使用
-
bufio.ScanBytes
-
按照字节为单位进行分割。
-
每次返回一个字节的
[]byte
。 -
func main() { input := "Hello, world!" scanner := bufio.NewScanner(strings.NewReader(input)) scanner.Split(bufio.ScanBytes) for scanner.Scan() { fmt.Printf("%q ", scanner.Bytes()) } if err := scanner.Err(); err != nil { fmt.Println("Error:", err) } } //"H" "e" "l" "l" "o" "," " " "w" "o" "r" "l" "d" "!"
-
-
bufio.ScanRunes
-
按照 Unicode 字符为单位进行分割。
-
每次返回一个 Unicode 字符的
[]rune
。 -
func main() { input := "世界" scanner := bufio.NewScanner(strings.NewReader(input)) scanner.Split(bufio.ScanRunes) for scanner.Scan() { fmt.Printf("%q ", scanner.Text()) } if err := scanner.Err(); err != nil { fmt.Println("Error:", err) } }//"世" "界"
-
-
bufio.ScanLines
-
按照行为单位进行分割。
-
每次返回一行数据,不包括行尾的换行符。
-
func main() { input := "Hello,\nworld!\n" scanner := bufio.NewScanner(strings.NewReader(input)) scanner.Split(bufio.ScanLines) for scanner.Scan() { fmt.Println(scanner.Text()) } if err := scanner.Err(); err != nil { fmt.Println("Error:", err) } } //Hello, //world!
-
-
bufio.ScanWords
-
按照单词为单位进行分割。
-
每次返回一个由非空白字符组成的连续片段。
-
func main() { input := "Hello world" scanner := bufio.NewScanner(strings.NewReader(input)) scanner.Split(bufio.ScanWords) for scanner.Scan() { fmt.Println(scanner.Text()) } if err := scanner.Err(); err != nil { fmt.Println("Error:", err) } } //Hello //world
-
-
bufio.ScanRegexp
-
按照正则表达式匹配的内容进行分割。
-
用户需要提供一个正则表达式作为参数。
-
func main() { input := "apple123banana456cherry" scanner := bufio.NewScanner(strings.NewReader(input)) scanner.Split(bufio.ScanRegexp(regexp.MustCompile(`\d+`)))//匹配一个或多个数字字符 for scanner.Scan() { fmt.Println(scanner.Text()) } if err := scanner.Err(); err != nil { fmt.Println("Error:", err) } } //123 //456
-
4、自定义分割符(token)使用
最后如果上面的几个封装的都不够用,可以通过自定义token
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 自定义分割函数,以逗号为分隔符
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
// 查找第一个逗号的位置
i := strings.IndexByte(string(data), ',')
if i >= 0 {
return i + 1, data[:i], nil
}
// 如果没有找到逗号,且已经到达输入流的末尾,则返回结束
if atEOF {
return len(data), data, nil
}
// 否则要求 Scan 方法继续读取数据
return 0, nil, nil
})
for scanner.Scan() {
fmt.Println("Token:", scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("Error:", err)
}
}
4、如果判断扫描到的数据是什么类型的数据
在 Go 中,标准输入的数据都是通过io.Reader以字节数组的形式暂存的。
那么怎么判断标准输入中的内容是字符串?还是整数?还是浮点数?..
-
可以通过strconv包的函数来做判断
1、判断是否为整数: val, err := strconv.Atoi(string(inputBytes)) if err == nil { // inputBytes 包含一个整数 } else { // inputBytes 不是整数 } 2、判断是否为浮点数: val, err := strconv.ParseFloat(string(inputBytes), 64) if err == nil { // inputBytes 包含一个浮点数 } else { // inputBytes 不是浮点数 } 3、将其视为字符串: inputStr := string(inputBytes) // 现在 inputStr 包含从标准输入读取的字符串数据
-
也可以使用类型断言从标准输入读取数据类型
func main() { reader := bufio.NewReader(os.Stdin) inputBytes, _ := reader.ReadBytes('\n') // 使用类型断言判断数据类型 switch v := interface{}(inputBytes).(type) { case int: fmt.Printf("Input is an integer: %d\n", v) case float64: fmt.Printf("Input is a float: %f\n", v) default: inputStr := string(inputBytes) fmt.Printf("Input is a string: %s\n", inputStr) } }