Go中fmt.Println覆盖bufio Scanner读取行及切片存储异常问题
问题原因拆解
1. 输出覆盖行的根源
你的文本文件用了**回车符(\r)**代替了标准的换行符(\n,Windows下一般是\r\n)。当fmt.Println输出带\r的内容时,这个符号会把终端光标拽回当前行开头,后面的内容直接覆盖前面的,看起来就像没换行一样。
2. 只存第一个条目的问题
十有八九是你的代码只调用了一次scanner.Scan(),没循环读取所有行。bufio.Scanner的Scan()每次只能读一行(按默认换行符分割),不循环的话自然只能拿到第一行内容;再加上文件里的\r搞事,后面的内容要么被当成同一行的一部分,要么打印时被覆盖,导致你以为只有第一个条目被存下来了。
解决步骤
先搞定换行符问题
给Scanner自定义分割规则,让它能识别\r、\n、\r\n这几种换行方式:
scanner := bufio.NewScanner(file) // 自定义分割函数,兼容各种换行符 scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { for i := 0; i < len(data); i++ { if data[i] == '\n' || data[i] == '\r' { // 处理Windows的\r\n组合 if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' { return i+2, data[:i], nil } return i+1, data[:i], nil } } if atEOF && len(data) > 0 { return len(data), data, nil } return 0, nil, nil })
再修复只读第一行的问题
必须用循环调用scanner.Scan()遍历所有行,把每行分割后的姓名都追加到切片里:
var names []string for scanner.Scan() { line := scanner.Text() fmt.Println(line) // 现在不会再覆盖了 // 把当前行的姓名分割后批量追加到切片 names = append(names, strings.Fields(line)...) } // 别忘了检查扫描过程中有没有出错 if err := scanner.Err(); err != nil { fmt.Println("读文件出问题了:", err) return }
完整修复后的代码
package main import ( "bufio" "fmt" "os" "strings" ) func main() { file, err := os.Open("names.txt") if err != nil { fmt.Println("打开文件失败:", err) return } defer file.Close() scanner := bufio.NewScanner(file) // 自定义分割函数兼容各种换行符 scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { for i := 0; i < len(data); i++ { if data[i] == '\n' || data[i] == '\r' { if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' { return i+2, data[:i], nil } return i+1, data[:i], nil } } if atEOF && len(data) > 0 { return len(data), data, nil } return 0, nil, nil }) var names []string for scanner.Scan() { line := scanner.Text() fmt.Println(line) names = append(names, strings.Fields(line)...) } if err := scanner.Err(); err != nil { fmt.Println("扫描文件出错:", err) return } fmt.Println("所有姓名:", names) }
内容的提问来源于stack exchange,提问作者Pavlo Maistrenko
相关产品推荐
相关产品推荐

