Go fmt.Fscanln 与 Fscan 对换行处理有什么区别
来源:17golang原创
时间:2026-10-04 16:32:45 100浏览 收藏
fmt.Fscan 会把输入中的换行视为普通空白,所以它可以跨行继续寻找下一个值;fmt.Fscanln 则把换行当作本次扫描的边界,遇到换行就停止,而且最后一个目标值后面必须是换行或 EOF。要读取“任意空白分隔的连续数据”选 Fscan,要保证“一次调用只读一行固定字段”选 Fscanln。
官方文档:https://pkg.go.dev/fmt
我第一次踩坑,是把“空白”误认为“不含换行”
我曾经处理一份每行一个整数的输入,想一次读取两个值。直觉上以为第一行只有一个值,Fscan 会在行尾停下;结果它把第二行的整数也读进来了。后来对照文档才确认:在 Fscan 看来,空格、制表符和换行都可以分隔连续值,行结构并不是它的约束。
Fscanln 的设计目标正好相反。它允许同一行内的空白分隔,但不会越过该行的换行去凑够参数。这个差异决定了两者不是“有没有 ln 的写法偏好”,而是两种输入模型。

同一份输入,两个函数会得到不同结果
用 "10\n20\n" 同时读取两个整数,差异最容易看清:
package main
import (
"fmt"
"strings"
)
func main() {
const input = "10\n20\n"
var a, b int
// Fscan 把换行当空白,因此可以跨到第二行读取 b
n1, err1 := fmt.Fscan(strings.NewReader(input), &a, &b)
fmt.Println("Fscan:", n1, err1, a, b)
var x, y int
// Fscanln 在第一行换行处停止,y 不会从第二行补齐
n2, err2 := fmt.Fscanln(strings.NewReader(input), &x, &y)
fmt.Println("Fscanln:", n2, err2, x, y)
}
Fscan 能成功扫描两个值;Fscanln 只完成第一个值,随后因遇到换行返回非空错误。错误文本不应拿来做程序分支,可靠依据是返回的 n、err 以及业务期望的字段数。
| 比较项 | Fscan | Fscanln |
|---|---|---|
| 换行语义 | 普通空白 | 本次扫描边界 |
| 参数不足时 | 可继续到下一行寻找 | 当前行结束即返回错误 |
| 最后一个参数后 | 不要求行结束 | 必须紧接换行或 EOF |
| 适合数据 | 任意空白分隔的 token 流 | 每行固定字段数的记录 |
一行固定字段时,Fscanln 很顺手
如果输入约定每行都是“名称 分数”,我会选择 Fscanln,因为它能把字段不足和字段过多都暴露出来。下面的循环接受最后一行没有换行的情况,因为官方契约允许最后一个参数后直接 EOF。
package main
import (
"errors"
"fmt"
"io"
"strings"
)
func readScores(text string) error {
r := strings.NewReader(text)
for lineNo := 1; ; lineNo++ {
var name string
var score int
// 每次调用只接受当前行的两个字段
n, err := fmt.Fscanln(r, &name, &score)
if errors.Is(err, io.EOF) && n == 0 {
return nil
}
if err != nil {
return fmt.Errorf("第 %d 行只读到 %d 个字段: %w", lineNo, n, err)
}
// 到这里说明当前行字段数和行尾都满足约定
fmt.Println(name, score)
}
}
这也是 Fscanln 给我的意外收获:它不只是“遇到换行停止”,还会检查最后一个参数后是否已经到达换行或 EOF。如果输入是 alice 90 extra,即使前两个变量已经赋值,额外 token 仍会导致错误。调用方必须同时检查 n 与 err,不能只看变量里有没有值。
支持边界:CRLF 可以,整行文本不适合
Go 的扫描函数把紧邻的 \r\n 当作普通换行处理,所以 Windows 文本与 Unix 文本在这里不需要分两套逻辑。但 Fscanln 仍然按空白切 token,它不会把“hello world”作为一个包含空格的完整字符串交给单个 string 变量。
如果目标是保留整行文本,我更倾向于 bufio.Scanner。它的默认 split 是按行,得到的 Text() 会保留行内空格:
package main
import (
"bufio"
"fmt"
"strings"
)
func readLines(text string) error {
scanner := bufio.NewScanner(strings.NewReader(text))
// 如需支持很长的行,应按业务上限调整缓冲区
scanner.Buffer(make([]byte, 1024), 1024*1024)
for scanner.Scan() {
// Text 返回完整一行,行内空格不会被拆成多个参数
fmt.Println(scanner.Text())
}
return scanner.Err()
}
我的选择原则是:字段已知且一行一条记录,用 Fscanln;只关心连续 token、不关心行,用 Fscan;文本本身以整行为单位,用 bufio.Scanner 或 bufio.Reader。

兼容处理:不要随意混用不同读取层
fmt 文档还提醒,Fscan 等函数可能多读一个 rune;如果传入的 Reader 实现了 ReadRune 和 UnreadRune,扫描器会利用它们保存多读字符。原始 Reader 不具备这些能力时,可以先统一包一层 bufio.NewReader。
这意味着,同一个输入不要一会儿直接交给 Fscan,一会儿又绕过缓冲层调用底层 Reader。对我来说,最稳妥的做法是只创建一个 bufio.Reader,后续所有扫描和逐行读取都围绕它组织;更简单的办法则是一个解析任务只选一种模型,不混用。
性能与错误处理要点
- 总是检查 n 和 err:部分变量可能已经成功赋值,不能把非零 n 当作整体成功。
- 不要依赖错误字符串:用
errors.Is(err, io.EOF)识别 EOF,其余错误带上下文返回。 - 明确字段数量:
Fscanln适合固定字段;可变字段行通常应先读整行再二次解析。 - 复用缓冲 Reader:避免多层读取器各自缓冲,导致后续读取位置不符合预期。
- 大量输入先评估:
fmt扫描方便但偏通用;极大数据量或严格格式可考虑专用解析器。
常见问题
Fscanln 能读取包含空格的整行字符串吗?
不能直接当整行读取。它仍按空白分隔参数,只是不会跨越换行。需要保留空格时用 bufio.Scanner 或 Reader 的逐行方法。
最后一行没有换行,Fscanln 会失败吗?
只要最后一个目标值后直接到 EOF,就符合契约,不要求文件一定以换行结尾。
Fscan 为什么读到了下一行?
因为换行对 Fscan 只是空白分隔符。若业务要求当前行字段不足就报错,应改用 Fscanln。
我的结论
Fscan 和 Fscanln 的核心差异只有一句话:前者忽略行边界,后者把行边界纳入正确性检查。输入是 token 流时用 Fscan 更自然;输入是每行固定结构时,Fscanln 能更早暴露脏数据;需要完整行文本时就不要勉强用 fmt 扫描。把输入模型先定清楚,比在报错后反复更换函数更省时间。
-
335 收藏
-
492 收藏
-
432 收藏
-
437 收藏
-
297 收藏
-
218 收藏
-
247 收藏
-
180 收藏
-
393 收藏
-
267 收藏
-
157 收藏
-
Golang · Go问答 | 3小时前 | 标准库 · HTTP服务 · 可观测性 · Go问答 · Go expvar http.ServeMux expvar.Handler /debug/vars 运行指标386 收藏
-
316 收藏
-
284 收藏
-
139 收藏
-
463 收藏
-
374 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习