Golang fmt.Fscanf 函数
最后修改时间 2025 年 5 月 8 日
本教程解释了如何在 Go 中使用 fmt.Fscanf 函数。我们将通过格式化扫描的实际示例来介绍输入解析基础知识。
fmt.Fscanf 函数根据格式说明符从读取器扫描输入。它会将空格分隔的值解析到变量中。这对于从文件或其他输入源读取结构化数据非常有用。
在 Go 中,fmt.Fscanf 类似于 fmt.Scanf,但它处理任何 io.Reader 而不是标准输入。它返回成功扫描的项目数以及遇到的任何错误。
基本的 Fscanf 示例
fmt.Fscanf 最简单的用法是从字符串读取器读取值。此示例演示了基本的格式化扫描。
注意: 格式字符串必须与输入结构完全匹配。
package main
import (
"fmt"
"strings"
)
func main() {
input := "John 25 50000.50"
reader := strings.NewReader(input)
var name string
var age int
var salary float64
n, err := fmt.Fscanf(reader, "%s %d %f", &name, &age, &salary)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Scanned %d items: %s, %d, %.2f\n",
n, name, age, salary)
}
该代码从输入中读取一个字符串、一个整数和一个浮点数。格式字符串指定了预期的类型,并且变量必须作为指针传递。
从文件读取
fmt.Fscanf 通常用于从文件读取结构化数据。此示例展示了如何解析文本文件中的数据。
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("data.txt")
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer file.Close()
var id int
var product string
var price float64
for {
n, err := fmt.Fscanf(file, "%d %s %f\n", &id, &product, &price)
if err != nil {
break
}
fmt.Printf("Read %d items: %d %s %.2f\n", n, id, product, price)
}
}
该代码在循环中从文件中读取多个记录。每行包含一个整数、一个字符串和一个浮点值,它们之间用空格分隔。
处理不同的格式
fmt.Fscanf 可以解析各种输入格式。此示例展示了不同的格式说明符的用法。
package main
import (
"fmt"
"strings"
)
func main() {
input := "Date: 2023-05-15 Time: 14:30 Value: 42.7%"
reader := strings.NewReader(input)
var year, month, day, hour, minute int
var value float64
var unit string
n, err := fmt.Fscanf(reader, "Date: %d-%d-%d Time: %d:%d Value: %f%%%s",
&year, &month, &day, &hour, &minute, &value, &unit)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Scanned %d items: %d-%02d-%02d %02d:%02d %.1f%%\n",
n, year, month, day, hour, minute, value)
}
格式字符串包含必须与输入完全匹配的文字文本。像 % 这样的特殊字符必须用另一个 % 在格式字符串中进行转义。
读取多行
fmt.Fscanf 可以通过在格式字符串中包含换行符来处理多行输入。此示例演示了多行解析。
package main
import (
"fmt"
"strings"
)
func main() {
input := `Person:
Name: Alice
Age: 30
Occupation: Engineer
`
reader := strings.NewReader(input)
var name, occupation string
var age int
_, err := fmt.Fscanf(reader, `Person:
Name: %s
Age: %d
Occupation: %s
`, &name, &age, &occupation)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("%s is a %d-year-old %s\n", name, age, occupation)
}
格式字符串包含换行符以匹配输入结构。反引号用于原始字符串字面量,以保留格式。
错误处理
正确处理错误在使用 fmt.Fscanf 时至关重要。此示例展示了如何处理各种扫描错误。
package main
import (
"fmt"
"strings"
)
func main() {
tests := []string{
"100 200", // OK
"100 abc", // type mismatch
"100", // not enough values
"100 200 300", // extra values
}
for _, input := range tests {
reader := strings.NewReader(input)
var a, b int
n, err := fmt.Fscanf(reader, "%d %d", &a, &b)
fmt.Printf("Input: %q\n", input)
fmt.Printf("Scanned %d items: %d, %d\n", n, a, b)
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println("-----")
}
}
该代码测试了不同的错误场景。fmt.Fscanf 返回成功扫描的项目数以及遇到的任何错误。
读取到结构体中
fmt.Fscanf 可以直接填充结构体字段。此示例展示了结构化数据解析。
package main
import (
"fmt"
"strings"
)
type Employee struct {
ID int
Name string
Salary float64
}
func main() {
input := "101 Bob 75000.50"
reader := strings.NewReader(input)
var emp Employee
n, err := fmt.Fscanf(reader, "%d %s %f",
&emp.ID, &emp.Name, &emp.Salary)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Scanned %d items: %+v\n", n, emp)
}
结构体字段作为指针传递给 fmt.Fscanf。%+v 动词会打印出带有字段名称的结构体。
自定义读取器示例
fmt.Fscanf 可与任何实现 io.Reader 的类型一起使用。此示例使用了自定义读取器实现。
package main
import (
"fmt"
"io"
)
type ByteReader struct {
data []byte
pos int
}
func (r *ByteReader) Read(p []byte) (n int, err error) {
if r.pos >= len(r.data) {
return 0, io.EOF
}
n = copy(p, r.data[r.pos:])
r.pos += n
return n, nil
}
func main() {
data := []byte("3.14 42 hello")
reader := &ByteReader{data: data}
var pi float64
var num int
var word string
n, err := fmt.Fscanf(reader, "%f %d %s", &pi, &num, &word)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Scanned %d items: %.2f %d %s\n", n, pi, num, word)
}
自定义的 ByteReader 实现 io.Reader 接口。fmt.Fscanf 可以像处理任何其他读取器一样处理它。
来源
本教程通过来自各种来源的格式化输入解析的实际示例,介绍了 Go 中的 fmt.Fscanf 函数。