ZetCode

Golang fmt.Fscanf 函数

最后修改时间 2025 年 5 月 8 日

本教程解释了如何在 Go 中使用 fmt.Fscanf 函数。我们将通过格式化扫描的实际示例来介绍输入解析基础知识。

fmt.Fscanf 函数根据格式说明符从读取器扫描输入。它会将空格分隔的值解析到变量中。这对于从文件或其他输入源读取结构化数据非常有用。

在 Go 中,fmt.Fscanf 类似于 fmt.Scanf,但它处理任何 io.Reader 而不是标准输入。它返回成功扫描的项目数以及遇到的任何错误。

基本的 Fscanf 示例

fmt.Fscanf 最简单的用法是从字符串读取器读取值。此示例演示了基本的格式化扫描。
注意: 格式字符串必须与输入结构完全匹配。

basic_fscanf.go
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 通常用于从文件读取结构化数据。此示例展示了如何解析文本文件中的数据。

file_fscanf.go
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 可以解析各种输入格式。此示例展示了不同的格式说明符的用法。

formats_fscanf.go
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 可以通过在格式字符串中包含换行符来处理多行输入。此示例演示了多行解析。

multiline_fscanf.go
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 时至关重要。此示例展示了如何处理各种扫描错误。

error_fscanf.go
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 可以直接填充结构体字段。此示例展示了结构化数据解析。

struct_fscanf.go
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 的类型一起使用。此示例使用了自定义读取器实现。

custom_reader.go
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 包文档

本教程通过来自各种来源的格式化输入解析的实际示例,介绍了 Go 中的 fmt.Fscanf 函数。

作者

我的名字是 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面有十多年的经验。

列出所有 Golang 教程