bufio.NewScanner 是 Go 语言中用于逐行读取数据的强大工具,它提供了高效、便捷的文本扫描功能。
基本用法
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
// 创建输入源
input := strings.NewReader("Hello\nWorld\nGo\n")
// 创建 Scanner
scanner := bufio.NewScanner(input)
// 逐行扫描
for scanner.Scan() {
fmt.Println(scanner.Text()) // 获取当前行的文本
}
// 检查错误
if err := scanner.Err(); err != nil {
fmt.Println("Error:", err)
}
}
核心方法
1. Scan() 方法
func (s *Scanner) Scan() bool
- 推进扫描器到下一个 token
- 返回
true如果找到 token,false如果到达输入末尾或发生错误 - 自动处理不同平台的换行符(
\n,\r\n)
2. Text() 方法
func (s *Scanner) Text() string
- 返回最近一次
Scan()调用产生的 token - token 是去除行分隔符后的字符串
3. Bytes() 方法
func (s *Scanner) Bytes() []byte
- 返回最近一次
Scan()调用产生的 token 的字节切片 - 注意:返回的字节切片在下一次
Scan()调用后可能失效
4. Err() 方法
func (s *Scanner) Err() error
- 返回扫描过程中遇到的第一个非 EOF 错误
高级配置
自定义分割函数
package main
import (
"bufio"
"fmt"
"strings"
)
// 按单词分割
func main() {
input := strings.NewReader("Hello World Go Programming")
scanner := bufio.NewScanner(input)
// 设置自定义分割函数(按空格分割)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
内置分割函数
// 按行分割(默认)
scanner.Split(bufio.ScanLines)
// 按单词分割
scanner.Split(bufio.ScanWords)
// 按字符分割
scanner.Split(bufio.ScanRunes)
// 按字节分割
scanner.Split(bufio.ScanBytes)
自定义分割函数示例
package main
import (
"bufio"
"bytes"
"fmt"
"strings"
)
// 自定义分割函数:按逗号分割
func commaSplit(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, ','); i >= 0 {
// 找到逗号,返回逗号前的部分
return i + 1, data[0:i], nil
}
// 如果到达文件末尾,返回剩余数据
if atEOF {
return len(data), data, nil
}
// 请求更多数据
return 0, nil, nil
}
func main() {
input := strings.NewReader("apple,banana,cherry,date")
scanner := bufio.NewScanner(input)
scanner.Split(commaSplit)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
缓冲区大小配置
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
input := strings.NewReader("Hello\nWorld\n")
scanner := bufio.NewScanner(input)
// 设置初始缓冲区大小(可选)
buffer := make([]byte, 0, 64*1024) // 64KB
scanner.Buffer(buffer, 1024*1024) // 最大1MB
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
实际应用示例
1. 读取文件
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("data.txt")
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineNumber := 1
for scanner.Scan() {
fmt.Printf("%d: %s\n", lineNumber, scanner.Text())
lineNumber++
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading file:", err)
}
}
2. 处理标准输入
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
fmt.Println("Enter text (Ctrl+D to end):")
for scanner.Scan() {
text := scanner.Text()
if text == "exit" {
break
}
fmt.Println("You entered:", text)
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
}
3. 统计文件信息
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("document.txt")
if err != nil {
panic(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
var lines, words, chars int
for scanner.Scan() {
line := scanner.Text()
lines++
chars += len(line)
// 简单统计单词数(按空格分割)
wordScanner := bufio.NewScanner(strings.NewReader(line))
wordScanner.Split(bufio.ScanWords)
for wordScanner.Scan() {
words++
}
}
fmt.Printf("Lines: %d, Words: %d, Characters: %d\n", lines, words, chars)
}
注意事项
-
性能考虑:对于大文件,Scanner 比
ioutil.ReadFile更节省内存 -
错误处理:总是检查
scanner.Err() -
缓冲区重用:
Text()和Bytes()返回的数据可能被后续扫描重用 - 长行处理:默认缓冲区大小为 64KB,对于超长行需要调整缓冲区大小
常见问题解决
处理超长行
scanner := bufio.NewScanner(file)
maxCapacity := 1024 * 1024 // 1MB
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)
性能优化
对于性能敏感的场景,可以考虑使用 bufio.Reader 的 ReadLine() 方法,但 Scanner 在大多数情况下已经足够高效且更易用。
bufio.NewScanner 是 Go 中处理文本输入的推荐方式,它提供了良好的性能、灵活的分割选项和简洁的 API。