Go fmt.Fscanln 与 Fscan 对换行处理有什么区别
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 扫描。把输入模型先定清楚,比在报错后反复更换函数更省时间。
78动漫新品速递怎么看?资讯、品类与发售状态核对说明
- 上一篇
- 78动漫新品速递怎么看?资讯、品类与发售状态核对说明
- 下一篇
- qooapp隐私政策和服务条款在哪里看?账号、设备与公开内容边界
-
- Golang · Go问答 | 21分钟前 | go ·
- Go constant.Value.ExactString 为什么会输出分数
- 180浏览 收藏
-
- Golang · Go问答 | 39分钟前 | 格式化 · Go问答 · Go go/ast token.Pos go/format ast.CommentMap go/printer
- Go ast.CommentMap 为什么格式化后注释会移动
- 393浏览 收藏
-
- Golang · Go问答 | 1小时前 | flag · go · 初始化 · Go 命令行参数 init flag.Parse
- Go flag.Parse 为什么不能在 init 中提前调用
- 157浏览 收藏
-
- Golang · Go问答 | 2小时前 | 标准库 · HTTP服务 · 可观测性 · Go问答 · Go expvar http.ServeMux expvar.Handler /debug/vars 运行指标
- Go expvar.Handler 与默认 /debug/vars 有什么区别
- 386浏览 收藏
-
- Golang · Go问答 | 2小时前 |
- Go 自定义 Is 方法为什么不能递归调用 Unwrap
- 316浏览 收藏
-
- Golang · Go问答 | 2小时前 | 错误处理 · go · Go error nil errors.Join
- Go errors.Join 全是 nil 时为什么返回 nil
- 284浏览 收藏
-
- Golang · Go问答 | 3小时前 | 标准库 · Go教程 · 输入校验 · Go encoding/xml xml.Decoder strict XML容错
- Go xml.Decoder.Strict 关闭后会容忍哪些格式问题
- 139浏览 收藏
-
- Golang · Go问答 | 3小时前 | go · encoding/json ·
- Go json.Decoder.UseNumber 为什么能避免浮点精度变化
- 463浏览 收藏
-
- Golang · Go问答 | 4小时前 | 序列化 · Go问答 · 错误排查 · Go encoding/gob register 类型未注册 接口字段 RegisterName
- Go gob 解码接口字段为什么提示类型未注册
- 374浏览 收藏
-
- Golang · Go问答 | 4小时前 | go · csv · Go encoding/csv csv.Reader LazyQuotes
- Go csv.Reader.LazyQuotes 会接受哪些非标准引号
- 486浏览 收藏
-
- Golang · Go问答 | 4小时前 |
- Go binary.Varint 返回负数读取长度是什么意思
- 187浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 326次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 385次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 376次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 343次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 170次使用
-
- go语言标准库fmt包的一键入门
- 2023-01-24 335浏览
-
- Go语言基础go fmt命令使用示例详解
- 2023-01-01 492浏览
-
- golang 占位符和fmt常见输出介绍
- 2023-02-16 432浏览
-
- golang fmt占位符的使用详解
- 2023-01-01 437浏览
-
- Go strings.Cut 和 Split 处理键值文本有什么区别
- 2026-09-14 297浏览

