fmt
包前言
fmt
包实现了类似C语言printf
和scanf
的格式化I/O。格式化动作('verb')源自C语言但更简单。fmt
包主要分为①向外输出内容和②获取输入内容两大部分
1. 输出(写入操作,与Writer
相关)
1.1 fmt.Print
系列函数
Println
:采用默认格式将其参数格式化并写入标准输出。总是会在相邻参数的输出之间添加空格并在输出结束后添加换行符。返回写入的字节数和遇到的任何错误。Printf
:根据format参数生成格式化的字符串并写入标准输出。返回写入的字节数和遇到的任何错误。
print
有三个相关的函数:
//type any = interface{},any表示任意类型
//...表示可以传入任意个参数
func Print(a ...any) (n int, err error) {
return Fprint(os.Stdout, a...)
}
func Println(a ...any) (n int, err error) {
return Fprintln(os.Stdout, a...)
}
//`format string`表示格式化的字符串
func Printf(format string, a ...any) (n int, err error) {
return Fprintf(os.Stdout, format, a...)
}
可以看出,这三个函数最终都调用了Fprint
系列函数,os.Stdout
(standard out)代表标准输出,即控制台输出。
1.2 格式化占位符
1.2.1 通用占位符
占位符 | 说明 |
---|---|
%v | 值的默认格式表示 |
%+v | 类似%v,但输出结构体时会添加字段名 |
%#v | 值的Go语法表示(所在包名+结构体名称) |
%T | 打印值的类型 |
%% | 打印百分号 |
测试用例:
测试文件代码所在文件结构:
type User struct {
Id int64
}
func TestPrintf(t *testing.T) {
user := &User{Id: 1}
fmt.Printf("%v\n", user) //&{1}
fmt.Printf("%+v\n", user) //&{Id:1}
fmt.Printf("%#v\n", user) //&fmttest.User{Id:1}
fmt.Printf("%T\n", user) //*fmttest.User
fmt.Printf("%%\n") //%
}
1.2.2 布尔型
占位符 | 说明 |
---|---|
%t | true或false |
测试用例:
func TestPrintf2(t *testing.T) {
fmt.Printf("%t\n", true)//true
}
1.2.3 整型
占位符 | 说明 |
---|---|
%b | 表示为二进制 |
%c | 该值对应的unicode 码值 |
%d | 表示为十进制 |
%o | 表示为八进制 |
%x | 表示为十六进制,使用a-f |
%X | 表示为十六进制,使用A-F |
%U | 表示为Unicode格式:U+1234,等价于“U+%04X” |
%q | 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示 |
测试用例:
func TestPrintf3(t *testing.T) {
n := 180
fmt.Printf("%b\n", n) //10110100
fmt.Printf("%c\n", n) //´
fmt.Printf("%d\n", n) //180
fmt.Printf("%o\n", n) //264
fmt.Printf("%x\n", n) //b4
fmt.Printf("%X\n", n) //B4
fmt.Printf("%U\n", n) //U+00B4
a := 96
fmt.Printf("%q\n", a) //'`'
fmt.Printf("%q\n", 0x4E2D) //'中'
}
1.2.4 浮点数与复数
占位符 | 说明 |
---|---|
%b | 无小数部分、二进制指数的科学计数法,如-123456p-78 |
%e | 科学计数法,如-1234.456e+78 |
%E | 科学计数法,如-1234.456E+78 |
%f | 有小数部分但无指数部分,如123.456 |
%F | 等价于%f |
%g | 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出) |
%G | 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出) |
测试用例:
func TestPrintf4(t *testing.T) {
f := 18.54
fmt.Printf("%b\n", f) //5218546068215562p-48
fmt.Printf("%e\n", f) //1.854000e+01
fmt.Printf("%E\n", f) //1.854000E+01
fmt.Printf("%f\n", f) //18.540000
fmt.Printf("%F\n", f) //18.540000
fmt.Printf("%g\n", f) //18.54
fmt.Printf("%G\n", f) //18.54
}
1.2.5 字符串和[]byte
占位符 | 说明 |
---|---|
%s | 直接输出字符串或者[]byte |
%q | 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示 |
%x | 每个字节用两字符十六进制数表示(a-f) |
%X | 每个字节用两字符十六进制数表示(A-F) |
测试用例:
func TestPrintf5(t *testing.T) {
s := "我是字符串"
b := []byte{65, 66, 67}
fmt.Printf("%s\n", s) //我是字符串
fmt.Printf("%s\n", b) //ABC
fmt.Printf("%q\n", s) //"我是字符串"
fmt.Printf("%x\n", s) //e68891e698afe5ad97e7aca6e4b8b2
fmt.Printf("%X\n", s) //E68891E698AFE5AD97E7ACA6E4B8B2
}
1.2.6 指针
占位符 | 说明 |
---|---|
%p | 表示为十六进制,并加上前导的0x |
1.2.7 宽度标识符
例:%10.2
宽度:通过一个紧跟在百分号后面的十进制数(例子中的10)指定,如果未指定宽度,则表示值时除必须之外不作填充。
精度:通过(可选的)宽度后和点号后的跟的十进制数(例子中的2)指定。若未指定精度,会使用默认精度;若点号后没有跟数字,表示精度为0。
占位符 | 说明 |
---|---|
%f | 默认宽度,默认精度 |
%10f | 宽度9,默认精度 |
%.2f | 默认宽度,精度2 |
%10.2f | 宽度9,精度2 |
%10.f | 宽度9,精度0 |
测试用例:
func TestPrintf6(t *testing.T) {
n := 13.16
fmt.Printf("%f\n", n) //13.160000
fmt.Printf("%10f\n", n) // 13.160000
fmt.Printf("%10s\n", "我是字符串") // 我是字符串
fmt.Printf("%.1f\n", n) //13.2
fmt.Printf("%10.2f\n", n) // 13.16
fmt.Printf("%10.f\n", n) // 13
}
1.2.8 其他flag
占位符 | 说明 |
---|---|
+ | 总是输出数值的正负号;对%q(%+q)会生成全部是ASCII字符的输出(通过转义) |
空格 | 对数值,正数前加空格而负数前加负号;对字符串采用%x或%X时(%x或%X)会给各打印的字节之间加空格 |
- | 在输出右边填充空白而不是默认的左边(即从默认的右对齐切换为左对齐) |
# | 八进制数前加0(%#o),十六进制数前加0x(%#x)或0x(%#X),指针去掉前面的0x(%#p)对%q(%#q),对%U(%#U)会输出空格和单引号括起来的go字面值 |
0 | 使用0而不是空格填充,对于数值类型会把填充的0放在正负号后面 |
测试用例:
func TestPrintf7(t *testing.T) {
s := "我是字符串"
fmt.Printf("% d\n", 10) // 10
fmt.Printf("%s\n", s) //我是字符串
fmt.Printf("%10s\n", s) // 我是字符串
fmt.Printf("%-10s\n", s) //我是字符串
fmt.Printf("%10.2f\n", 10.14) // 10.14
fmt.Printf("%-10.2f\n", 10.14) //10.14
fmt.Printf("%010s\n", s) //00000我是字符串
}
1.3 fmt.Fprint
系列函数
Fprint
系列函数功能:
Fprint
:采用默认格式将其参数格式化并写入w。如果两个相邻的参数都不是字符串,会在它们的输出之间添加空格。返回写入的字节数和遇到的任何错误。Fprintln
:采用默认格式将其参数格式化并写入w。总是会在相邻参数的输出之间添加空格并在输出结束后添加换行符。返回写入的字节数和遇到的任何错误。Fprintf
:根据format参数生成格式化的字符串并写入w。返回写入的字节数和遇到的任何错误。【注】
Fprint
系列与
Fprint
系列:Writer
类型变量wos.stdout
Fprint
系列函数用在写文件中。
Fprint
有三个相关的函数:
func Fprint(w io.Writer, a ...any) (n int, err error) {
p := newPrinter()
p.doPrint(a)
n, err = w.Write(p.buf)
p.free()
return
}
func Fprintln(w io.Writer, a ...any) (n int, err error) {
p := newPrinter()
p.doPrintln(a)
n, err = w.Write(p.buf)
p.free()
return
}
func Fprintf(w io.Writer, format string, a ...any) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
n, err = w.Write(p.buf)
p.free()
return
}
1.3.1 测试用法1:用Fprint
向控制台输出内容
func TestFprint1(t *testing.T) {
fmt.Fprint(os.Stdout, "向控制台输出写入的字符串\n")
fmt.Fprintln(os.Stdout, "向控制台输出写入的字符串")
fmt.Fprintf(os.Stdout, "%s向控制台输出写入的字符串", "张三")
}
控制台输出:
向控制台输出写入的字符串
向控制台输出写入的字符串
张三向控制台输出写入的字符串
【预备知识】
os.OpenFile(name string, flag int, perm FileMode)
的参数:name
:文件名,flag
:操作方式,perm
:文件权限;关于flag参数,操作方式列表如下:
const ( O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件 O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件 O_RDWR int = syscall.O_RDWR // 读写模式打开文件 O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部 O_CREATE int = syscall.O_CREAT // 如果不存在将创建一个新文件 O_EXCL int = syscall.O_EXCL // 和O_CREATE配合使用,文件必须不存在 O_SYNC int = syscall.O_SYNC // 打开文件用于同步I/O O_TRUNC int = syscall.O_TRUNC // 如果可能,打开时清空文件 )
【注意】可以选择多个操作方式,用“|”隔开。
关于
perm
参数:
- 参数共4位,第1位总为0,表示八进制;第2到4位表示权限,第2位表示是否可读、第3位表示是否可写、第4位表示是否可执行;
- 例如:0000 不可读写、不可执行; 0001 不可读写 可执行; 0010 可写不可读,不可执行; 0100 可读不可写,不可执行;由于是二进制表示,所以可以将其转化为10进制的数值,比如0100可以转化为4, 0111为7,0110为6;也可以记作:4读2写1执行; 最多不大于7,在表示权限的时候可以使用8进制,来区分用户/用户组/其他用户的分别权限;比如0764表示文件所属用户可读可写可执行,用户组其他用户仅可读可写,其他非用户组用户仅可读;
- 查看权限可以用
os.FileMode(perm).String
来在控制台进行打印。如:func TestFprint3(t *testing.T) { s := os.FileMode(0777).String() fmt.Println(s) }
控制台输出:
-rwxrwxrwx
-rwxrwxrwx
表示普通文件,r表示可读,w表示可写,x表示可执行。
- 第1位:文件属性,一般常用的是“ - ”,表示是普通文件;“ d ”表示是一个目录。
- 第2~4位:文件所有者的权限
rwx
(可读/可写/可执行)。- 第5~7位:文件所属用户组的权限
rwx
(可读/可写/可执行)。- 第8~10位:其他人的权限
rwx
(可读/可写/可执行)。- 0777表示:创建了一个普通文件,所有人拥有所有的读、写、执行权限;
0666表示:创建了一个普通文件,所有人拥有对该文件的读、写权限,但是都不可执行;
0644表示:创建了一个普通文件,文件所有者对该文件有读写权限,用户组和其他人只有读权限,都没有执行权限。
1.3.2 测试用法2:用Fprint
向文件写入内容
func TestFprint2(t *testing.T) {
file, err := os.OpenFile("test.txt", os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
panic(err)
}
fmt.Fprintf(file, "%s向文件写入了若干内容", "张三")
}
程序运行会生成一个test.txt文件,文件内容如下:
os.O_APPEND
表示若再次运行内容则会继续在文件尾部添加,多次运行程序后的test.txt文件内容如下:
1.3.3 测试用法3:通过http
库的一些函数和fmt.Fprint
在web服务上的应用
实现一个需求:向浏览器页面输出文本内容。
package main
import (
"fmt"
"net/http"
)
func main() {
//设置通过/greetToTheWorld来访问一个传入的匿名函数
http.HandleFunc("/greet", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "hello,world!")
})
//设置访问端口,可以用localhost:8080/greet或127.0.0.1:8080/greet访问远程调用的函数
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println("error is ", err)
}
}
浏览器端:
1.4 fmt.Sprint
系列函数
Sprint
系列函数功能:
Sprint
:采用默认格式将其参数格式化,串联所有输出生成并返回一个字符串。如果两个相邻的参数都不是字符串,会在它们的输出之间添加空格。Sprintln
:采用默认格式将其参数格式化,串联所有输出生成并返回一个字符串。总是会在相邻参数的输出之间添加空格并在输出结束后添加换行符。Sprintf
:根据format参数生成格式化的字符串并返回该字符串。
Sprint
有三个相关的函数:
func Sprint(a ...any) string {
p := newPrinter()
p.doPrint(a)
s := string(p.buf)
p.free()
return s
}
func Sprintln(a ...any) string {
p := newPrinter()
p.doPrintln(a)
s := string(p.buf)
p.free()
return s
}
func Sprintf(format string, a ...any) string {
p := newPrinter()
p.doPrintf(format, a)
s := string(p.buf)
p.free()
return s
}
当需要将不同类型的数据类型拼接成字符串时,需要将所有数据都先转换成为字符串再进行拼接,这样会比较麻烦;
Sprint
系列函数被用来将若干不同数据类型的数据进行拼接,用起来会非常方便。
例如将字符串类型和整型拼接到一起:
func TestSprint(t *testing.T) {
host := "localhost"
port1 := 8080
port2 := 8090
port3 := 6379
addr1 := fmt.Sprintf("%s:%d", host, port1)
addr2 := fmt.Sprintln(host, ":", port2)
addr3 := fmt.Sprint(host, ":", port3)
fmt.Println(
addr1,
addr2,
addr3,
)
}
控制台输出:
localhost:8080 localhost : 8090
localhost:6379
1.5 fmt.Errorf
功能:根据format参数生成格式化字符串并返回一个包含该字符串的错误。
func Errorf(format string, a ...any) error {
p := newPrinter()
p.wrapErrs = true
p.doPrintf(format, a)
s := string(p.buf)
var err error
if p.wrappedErr == nil {
err = errors.New(s)
} else {
err = &wrapError{s, p.wrappedErr}
}
p.free()
return err
}
测试用例:
func TestErrorf(t *testing.T) {
err := fmt.Errorf("用户名格式错误:%s", "%$#!@")
if err != nil {
fmt.Println(err)
}
}
控制台输出:
用户名格式错误:%$#!@
【扩展】
errors.New
与Errorf
功能类似,以下为原码:func New(text string) error { return &errorString{text} }
可以看出
errors.New
仅支持输入字符串,而Errorf
可以通过格式化字符来输入不同的数据类型,所以后者用法更为灵活。
2 输入(读取操作,与Reader
相关)
2.1 fmt.Scan
系列函数
fmt.Scan
系列函数功能:
Scan
:从标准输入扫描文本,将成功读取的空白分隔的值保存进成功传递给本函数的参数。换行视为空白。返回成功扫描的条目个数和遇到的任何错误。如果读取的条目比提供的参数少,会返回一个错误报告原因。Scanln
:类似Scan
,但会在换行时才停止扫描。最后一个条目后必须有换行或者到达结束位置。Scanf
:从标准输入扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。(【注】Scanf
参数中若出现多个值则要用空格隔开)
Scan
有三个相关的函数:
func Scan(a ...any) (n int, err error) {
return Fscan(os.Stdin, a...)
}
func Scanln(a ...any) (n int, err error) {
return Fscanln(os.Stdin, a...)
}
func Scanf(format string, a ...any) (n int, err error) {
return Fscanf(os.Stdin, format, a...)
}
可以看出,这三个函数最终都调用了Fscan
系列函数,os.Stdin
(standard in)代表标准输入,即控制台输入。
[tips.]
Scan()
与Scanln()
的区别
Scan()
与Scanln()
用法与功能效果完全一样,但只有一个不同点:
Scan()
要求,若要输入两个值,那就必须输入两个,若要输入三个值,那就必须是三个。否则程序会一直等待,不会执行下面的程序;Scanln()
没有这个强制的要求,如果要求我们输入两个值,但只输入一个值,那么回车后程序依然会执行。总结:
Scan()
必须等待所要求的值输入完成才能执行程序,但是Scanln()
不管输入的数据是否完成,只要回车就直接执行。
Scanf
的特点
- 如果在
Scanf()
中写了两个输入值但是只输入一个,那样未输入的值就是该值类型的默认值;Scanf
支持用户定制化输入,即输入时必须按照设置好的参数格式来对数据进行输入。
测试用例1:Scan()
fmt.Print("please enter the userName and age:")
var userName string
var age int
count, _ := fmt.Scan(&userName, &age)
fmt.Println("the userName is:", userName, "\nthe age is:", age)
fmt.Println("user has entered", count, "values")
控制台输入liam 23
后:
please enter the userName and age:liam 23
the userName is: liam
the age is: 23
user has entered 2 values
测试用例2.1:Scanf()
常规用法
fmt.Print("please enter the userName:")
var userName string
fmt.Scanf("%s", &userName)
fmt.Println("the userName is:", userName)
控制台输入liam
后:
please enter the userName:liam
the userName is: liam
测试用例2.2:Scanf()
定制化输入
var (
name string
age int
marriage bool
)
fmt.Print("please enter the information:")
fmt.Scanf("1:%s 2:%d 3:%t", &name, &age, &marriage)//Scanf参数中若出现多个值则要用空格隔开
fmt.Printf("name:%s, age:%d, married:%t", name, age, marriage)
所谓定制化输入,就是在控制台输入数据时必须按照设置好的参数格式来对数据进行输入,不然变量就无法接收输入的数据。
控制台输入1:liam 2:23 3:false
后控制台输出:
name:liam, age:23, married:false
2.2 fmt.Fscan
系列函数
fmt.Fscan
系列函数功能:
Fscan
:从r扫描文本,将成功读取的空白分隔的值保存进成功传递给本函数的参数。换行视为空白。返回成功扫描的条目个数和遇到的任何错误。如果读取的条目比提供的参数少,会返回一个错误报告原因。Fscanln
:类似Fscan
,但会在换行时才停止扫描。最后一个条目后必须有换行或者到达结束位置。Fscanf
:从r扫描文本,根据format 参数指定的格式将成功读取的空白分隔的值保存进成功传递给本函数的参数。返回成功扫描的条目个数和遇到的任何错误。(【注】Fscanf
参数中若出现多个值则要用空格隔开)【注】
Fscan
系列与Scan
系列的区别就在于读取对象不同,两者的读取对象:
Fscan
系列:Reader
类型变量rScan
系列:标准输入os.stdin
Fprint
系列函数用在读文件中。
Fscan
有三个相关的函数:
func Fscan(r io.Reader, a ...any) (n int, err error) {
s, old := newScanState(r, true, false)
n, err = s.doScan(a)
s.free(old)
return
}
func Fscanln(r io.Reader, a ...any) (n int, err error) {
s, old := newScanState(r, false, true)
n, err = s.doScan(a)
s.free(old)
return
}
func Fscanf(r io.Reader, format string, a ...any) (n int, err error) {
s, old := newScanState(r, false, false)
n, err = s.doScanf(format, a)
s.free(old)
return
}
测试用例1:
【预备知识】
strings
包中有一个函数,可以用来创建一个Reader
类型。
函数名 功能 func NewReader(s string) *Reader
创建一个从s读取数据的 Reader
。本函数类似bytes.NewBufferString
,但是更有效率,且为只读的。
var (
name string
age int
marriage bool
)
//Fscan
r1 := strings.NewReader("Liam 23 false")
fmt.Fscan(r1, &name, &age, &marriage)
fmt.Printf("name:%s,age:%d,marriaged:%t\n", name, age, marriage)
//Fscanf
r2 := strings.NewReader("sprite : 28 : true")
fmt.Fscanf(r2, "%s : %d : %t", &name, &age, &marriage)
fmt.Printf("name:%s,age:%d,marriaged:%t", name, age, marriage)
控制台输出:
name:Liam,age:23,marriaged:false
name:sprite,age:28,marriaged:true
标签:err,int,fmt,func,Printf,字符串
From: https://www.cnblogs.com/SpriteLee/p/16837318.html