Go 语言标准库之 fmt 包

Go 语言的 fmt 包实现了类似 C 语言 printf 和 scanf 的格式化 I/O,主要分为向外输出内容和获取输入内容两大部分。

Print 类型

Print 类型函数会将内容格式化,并写入标准输出。主要有以下三个函数:

// 采用默认格式将参数格式化,并写入标准输出。如果两个相邻的参数都不是字符串,会在它们的输出之间添加空格
// 返回写入的字节数和遇到的任何错误
func Print(a ...interface{}) (n int, err error)

// 采用默认格式将参数格式化,并写入标准输出。总是会在相邻参数的输出之间添加空格,并在输出结束后添加换行符
// 返回写入的字节数和遇到的任何错误
func Println(a ...interface{}) (n int, err error)

// 根据 format 参数生成格式化的字符串,并写入标准输出
// 返回写入的字节数和遇到的任何错误
func Printf(format string, a ...interface{}) (n int, err error)

☕️ 示例代码

package main

import "fmt"

func main() {
    name, age, height := "Alice", 18, 170.3

    // Print
    fmt.Print(name, age)   // Alice18
    fmt.Print(age, height) // 18 170.3

    // Println
    fmt.Println(name, age) // Alice 18

    // Printf
    fmt.Printf("name:%s, age:%d, height:%f", name, age, height) // name:Alice, age:18, height:170.300000
}

Fprint 类型

Fprint 类型函数会将内容格式化,并写入到一个io.Writer接口类型的变量 w 中,可以使用 Fprint 类型函数往文件中写入内容。主要有以下三个函数:

// 采用默认格式将参数格式化,并写入 w。如果两个相邻的参数都不是字符串,会在它们的输出之间添加空格
// 返回写入的字节数和遇到的任何错误
func Fprint(w io.Writer, a ...interface{}) (n int, err error)

// 采用默认格式将参数格式化,并写入 w。总是会在相邻参数的输出之间添加空格,并在输出结束后添加换行符
// 返回写入的字节数和遇到的任何错误
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)

// 根据 format 参数生成格式化的字符串,并写入 w
// 返回写入的字节数和遇到的任何错误
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)

⭐️ 示例代码

package main

import (
    "fmt"
    "os"
)

func main() {
    // Fprintln,向标准输出写入内容
    _, err := fmt.Fprintln(os.Stdout, "你好,世界")
    if err != nil {
        return
    }

    // Fprint,向标准输出写入内容
    _, err = fmt.Fprint(os.Stdout, "hello world")
    if err != nil {
        return
    }

    // Fprintf,向文件中写入内容
    file, err := os.OpenFile("./test.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
    if err != nil {
        fmt.Println("open file failed!, err:", err)
        return
    }
    defer file.Close()
    context := "hello world"
    fmt.Fprintf(file, "往文件写入的文件内容是:%s", context)
}

// 控制台输出:
// 你好,世界
// hello world

// 当前目录下生成的 test.txt 内容为:
// 往文件写入的文件内容是:hello world

Sprint 类型

Sprint 类型函数会将内容格式化,并返回一个字符串,可以使用 Sprint 类型函数将其它类型转换为字符串类型。主要有以下三个函数:

// 采用默认格式将参数格式化,串联所有输出生成并返回一个字符串
// 如果两个相邻的参数都不是字符串,会在它们的输出之间添加空格
func Sprint(a ...interface{}) string

// 采用默认格式将参数格式化,串联所有输出生成并返回一个字符串
// 总是会在相邻参数的输出之间添加空格,并在输出结束后添加换行符
func Sprintln(a ...interface{}) string

// 根据 format 参数生成格式化的字符串,并返回该字符串
// 返回写入的字节数和遇到的任何错误
func Sprintf(format string, a ...interface{}) string

✏️ 示例代码

package main

import "fmt"

func main() {
    name, age, height := "Alice", 18, 170.3

    // Sprint
    fmt.Println(fmt.Sprint(name, age))   // Alice18
    fmt.Println(fmt.Sprint(age, height)) // 18 170.3

    // Sprintln
    fmt.Println(fmt.Sprintln(name, age)) // Alice 18

    // Sprintf
    fmt.Println(fmt.Sprintf("name:%s, age:%d, height:%f", name, age, height)) // name:Alice, age:18, height:170.300000
}

Errof 类型

// 根据 format 参数生成格式化字符串并返回一个包含该字符串的错误
func Errorf(format string, a ...interface{}) error

???? 示例代码

func main() {
    // 可以使用这种方式来格式化错误信息
    str := "这是一个错误"
    err := fmt.Errorf("错误原因:%s", str) // 错误原因:这是一个错误
    fmt.Println(err.Error())
}

Stringer 接口

如果某种数据类型实现了 Stringer 接口,那么该值在格式化输出时会调用String()方法进行处理,和 Java 的toString()方法类似。

type Stringer interface {
    String() string
}

✌ 示例代码

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    Sex  int
}

func (p *Person) String() string {
    return "This is a test!!!"
}

func main() {
    fmt.Println(&Person{}) // This is a test!!!
}

占位符

前面的*printf函数都是使用自定义的 format 格式对参数进行格式化,下面是一些常用的格式化占位符。

普通占位符

占位符说明
%v以默认方式打印变量的值
%+v类似%v,但打印结构体时会添加字段名
%#v在打印结构体时,会添加字段名和包名
%T打印变量的类型
%%打印百分号,字面上的百分号,并非值的占位符

✍ 示例代码

package main

import "fmt"

type Student struct {
    age    int
    height float32
}

func main() {
    Alice := Student{18, 170.3}

    fmt.Printf("%v\n", Alice)  // {18 170.3}
    fmt.Printf("%+v\n", Alice) // {age:18 height:170.3}
    fmt.Printf("%#v\n", Alice) // main.Student{age:18, height:170.3}
    fmt.Printf("%T\n", Alice)  // main.Student
    fmt.Printf("%%\n")         // %
}

布尔值占位符

占位符说明
%t打印 true 或 false

???? 示例代码

package main

import "fmt"

func main() {
    flag := true
    fmt.Printf("%t", flag) // true
}

整数占位符

占位符说明
%c该值对应的 unicode 码值
%b表示为二进制
%o表示为八进制,不加前缀 0
%#o表示为二进制,加前缀 0
%d表示为十进制
%+d表示为十进制,加数值的正负号前缀
%x表示为十六进制,使用a-f,不加前缀0x
%#x表示为十六进制,使用a-f,加前缀0x
%X表示为十六进制,使用A-F,不加前缀0X
%#X表示为十六进制,使用A-F,加前缀0X
%U表示为 unicode 格式:U+1234,等价于U+%04X
%q该值对应的单引号括起来的 Go 语法字符字面值,必要时会采用安全的转义表示

☕️ 示例代码

package main

import "fmt"

func main() {
    num := 95
    fmt.Printf("%c\n", num)  // _
    fmt.Printf("%b\n", num)  // 1011111
    fmt.Printf("%o\n", num)  // 137
    fmt.Printf("%#o\n", num) // 0137
    fmt.Printf("%d\n", num)  // 95
    fmt.Printf("%+d\n", num) // +95
    fmt.Printf("%x\n", num)  // 5f
    fmt.Printf("%#x\n", num) // 0x5f
    fmt.Printf("%X\n", num)  // 5F
    fmt.Printf("%#X\n", num) // 0X5F
    fmt.Printf("%U\n", num)  // U+005F
    fmt.Printf("%q\n", num)  // '_'
}

浮点数和复数占位符

占位符说明
%b无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat
%e=%.6e)科学计数法,使用 e,如-1234.456e+78
%E科学计数法,使用 E,如-1234.456E+78
%f=%.6f)有小数部分但无指数部分,如123.456
%F等价于%f
%g根据实际情况采用 %e%f 格式,以产生更紧凑的(无末尾的0)输出
%G根据实际情况采用 %E 或 %F 格式,以产生更紧凑的(无末尾的0)输出

⭐️ 示例代码

package main

import "fmt"

func main() {
    f := 1234.56
    fmt.Printf("%b\n", f) // 5429652300748554p-42
    fmt.Printf("%e\n", f) // 1.234560e+03
    fmt.Printf("%E\n", f) // 1.234560E+03
    fmt.Printf("%f\n", f) // 1234.560000
    fmt.Printf("%F\n", f) // 1234.560000
    fmt.Printf("%g\n", f) // 1234.56
    fmt.Printf("%G\n", f) // 1234.56
}

字符串和[]byte占位符

占位符说明
%s直接输出字符串表示
%q该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
%x十六进制表示,每个字节使用两字符(使用a-f)
%X十六进制表示,每个字节使用两字符(使用A-F)

✏️ 示例代码

package main

import "fmt"

func main() {
    s := "hello world"
    arr := []byte(s)

    // 直接输出字符串表示
    fmt.Printf("%s\n", s)   // hello world
    fmt.Printf("%s\n", arr) // hello world

    // 双引号括起来的 go 语法字符串字面值
    fmt.Printf("%q\n", s)   // "hello world"
    fmt.Printf("%q\n", arr) // "hello world"

    // 十六进制表示,每个字节使用两字符(使用a-f)
    fmt.Printf("%x\n", s)   // 68656c6c6f20776f726c64
    fmt.Printf("%x\n", arr) // 68656c6c6f20776f726c64

    // 十六进制表示,每个字节使用两字符(使用A-F)
    fmt.Printf("%X\n", s)   // 68656C6C6F20776F726C64
    fmt.Printf("%X\n", arr) // 68656C6C6F20776F726C64
}

指针占位符

占位符说明
%p十六进制表示,加上前缀0x
%#p十六进制表示,不加前缀0x

???? 示例代码

package main

import "fmt"

func main() {
    num := 0
    fmt.Printf("%p\n", &num)  // 0xc000014088
    fmt.Printf("%#p\n", &num) // c000014088
}

宽度和精度标识符

宽度通过一个紧跟在百分号后面的十进制数指定,如果未指定宽度,则表示值时除必需之外不作填充。精度通过(可选的)宽度后跟点号后跟的十进制数指定。如果未指定精度,会使用默认精度;如果点号后没有跟数字,表示精度为0。举例如下:

占位符说明
%s直接输出字符串表示
%10s输出字符串最小宽度为 10(右对齐)
%-10s输出字符串最小宽度为 10(左对齐)
%.5s输出字符串最大宽度为 5(右对齐)
%5.10s输出字符串最小宽度为 5,最大宽度为 10(右对齐)
%-5.10s输出字符串最小宽度为 5,最大宽度为 10(左对齐)
%5.3s输出字符串宽度为 5,如果原字符串宽度大于 3,则截断(右对齐)
%010s如果宽度小于10,就会在字符串前面补零(右对齐)
%f等价于%.6f,默认最小宽度为 0(右对齐),默认精度为 6
%9f输出最小宽度为 9(右对齐),默认精度为 6
%.2f默认最小宽度为 0(右对齐),精度为 2
%9.2f输出最小宽度为 9(右对齐),精度为 2
%9.f输出最小宽度为 9(右对齐),精度为 0

✌ 示例代码

package main

import "fmt"

func main() {
    str := "abcdefg"
    fmt.Printf("%s\n", str)     // abcdefg
    fmt.Printf("%10s\n", str)   //   abcdefg
    fmt.Printf("%-10s\n", str)  // abcdefg
    fmt.Printf("%.5s\n", str)   // abcde
    fmt.Printf("%5.10s\n", str) // abcdefg
    fmt.Printf("%5.3s\n", str)  //   abc
    fmt.Printf("%010s\n", str)  // 000abcdefg

    f := 1234.56
    fmt.Printf("%f\n", f)    // 1234.560000
    fmt.Printf("%9f\n", f)   // 1234.560000
    fmt.Printf("%.2f\n", f)  // 1234.56
    fmt.Printf("%9.2f\n", f) //   1234.56
    fmt.Printf("%9.f\n", f)  //      1235
}

Scanning

Scan 类型

Scan 类型函数从标准输入中扫描文本,将成功读取的值保存在传递给函数的参数中。主要有以下三个函数:

// 从标准输入扫描文本,将成功读取的空白符分隔的值保存进传递给本函数的参数中(换行视为空白)
// Scan 方法读取了和提供的参数相同个数的数据后,就会停止扫描
// 返回成功扫描的数据个数和遇到的任何错误。如果读取的数据个数比提供的参数少,会返回一个错误报告原因
func Scan(a ...interface{}) (n int, err error)

// 类似 Scan,不过它在遇到换行时就停止扫描,最后一个数据后面必须有换行或者到达结束位置
// 返回成功扫描的数据个数和遇到的任何错误
func Scanln(a ...interface{}) (n int, err error)

// 从标准输入扫描文本,根据 format 参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中
// 返回成功扫描的数据个数和遇到的任何错误
func Scanf(format string, a ...interface{}) (n int, err error)

✍ 示例代码

package main

import "fmt"

func main() {
    var (
        name   string
        age    int
        height float32
    )

    // Scan 从标准输入中扫描用户输入的数据,将以空白符分隔的数据分别存入指定的参数
    fmt.Scan(&name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f\n", name, age, height)

    // Scanln 从标准输入中扫描用户输入的数据,遇到回车就结束扫描(比较常用)
    fmt.Scanln(&name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f\n", name, age, height)

    // Scanf 为输入数据指定了具体输入格式,只有按照具体格式输入数据才会被扫描并存入对应变量
    fmt.Scanf("name:%s age:%d height:%f", &name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f\n", name, age, height)
}

// 控制台输入:
// Alice 18 170.3
// Bob 19 180.6
// name:Allan age:20 height:181.1

// 控制台输出:
// name:Alice age:18 height:170.300003
// name:Bob age:19 height:180.600006
// name:Allan age:20 height:181.100006

Fscan 类型

Fsan 类型函数会从io.reader接口类型的变量 r 中扫描文本,将成功读取的值保存在传递给函数的参数中。主要有以下三个函数:

// 从 r 中扫描文本,将成功读取的空白符分隔的值保存进传递给本函数的参数中(换行视为空白)
// Fscan 方法读取了和提供的参数相同个数的数据后,就会停止扫描
// 返回成功扫描的数据个数和遇到的任何错误。如果读取的数据个数比提供的参数少,会返回一个错误报告原因
func Fscan(r io.Reader, a ...interface{}) (n int, err error)

// 类似 Fscan,它在遇到换行时才停止扫描,最后一个数据后面必须有换行或者到达结束位置
// 返回成功扫描的数据个数和遇到的任何错误
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)

// 从 r 中扫描文本,根据 format 参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中
// 返回成功扫描的数据个数和遇到的任何错误
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)

???? 示例代码

package main

import (
    "fmt"
    "os"
)

func main() {
    var (
        name   string
        age    int
        height float32
    )

    // Fscan,从标准输入中读取用户输入的数据
    fmt.Fscan(os.Stdin, &name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f \n", name, age, height)

    // Fscanln,从标准输入中读取用户输入的数据
    fmt.Fscanln(os.Stdin, &name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f \n", name, age, height)

    // Fscanf,从文件中读取用户输入的数据
    file, err := os.Open("./test.txt")
    if err != nil {
        fmt.Println("open file failed!, err:", err)
        return
    }
    defer file.Close()
    fmt.Fscanf(file, "name:%s age:%d height:%f", &name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f\n", name, age, height)
}

// 控制台输入:
// Alice 18 170.3
// Bob 19 180.6

// 当前目录下 ./text.txt 内容更改为:
// name:Allan age:20 height:181.1

// 控制台输出:
// name:Alice age:18 height:170.300003
// name:Bob age:19 height:180.600006
// name:Allan age:20 height:181.100006

Sscan 类型

Sscan 类型函数会字符串 str 中扫描文本,将成功读取的值保存在传递给函数的参数中。主要有以下三个函数:

// 从 str 中扫描文本,将成功读取的空白符分隔的值保存进传递给本函数的参数中(换行视为空白)
// Sscan 方法读取了和提供的参数相同个数的数据后,就会停止扫描
// 返回成功扫描的数据个数和遇到的任何错误。如果读取的数据个数比提供的参数少,会返回一个错误报告原因
func Sscan(str string, a ...interface{}) (n int, err error)

// 类似 Sscan,它在遇到换行时才停止扫描,最后一个数据后面必须有换行或者到达结束位置
// 返回成功扫描的数据个数和遇到的任何错误
func Sscanln(str string, a ...interface{}) (n int, err error)

// 从 str 中扫描文本,根据 format 参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中
// 返回成功扫描的数据个数和遇到的任何错误
func Sscanf(str string, format string, a ...interface{}) (n int, err error)

☕️ 示例代码

package main

import "fmt"

func main() {
    var (
        name   string
        age    int
        height float32
    )

    // Sscan,从 str1 中读取文本
    str1 := "Alice 18 170.3"
    fmt.Sscan(str1, &name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f\n", name, age, height)

    // Sscanln,从 str2 中读取文本
    str2 := "Bob 19 180.6\naaa"
    fmt.Sscanln(str2, &name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f\n", name, age, height)

    // Sscanf,从 str3 中扫描文本,只有按照具体格式输入数据才会被扫描并存入对应变量
    str3 := "name:Allan age:20 height:181.1"
    fmt.Sscanf(str3, "name:%s age:%d height:%f", &name, &age, &height)
    fmt.Printf("name:%s age:%d height:%f\n", name, age, height)
}

// 控制台输出:
// name:Alice age:18 height:170.300003
// name:Bob age:19 height:180.600006
// name:Allan age:20 height:181.100006

参考

  1. Go语言fmt.Printf使用指南
  2. Go语言fmt包详解