本文最初发表在我的个人博客,查看原文,获得更好的阅读体验
Go推荐大家使用相同风格的代码格式,而不是在这个问题上花费过多的时间。为了统一代码格式,Go甚至自带了一个格式化工具gofmt(或者go fmt),该工具会从包级别而非源文件以标准样式的缩进和垂直对齐方式对源代码进行格式化,甚至必要情况下注释也会重新格式化。 也就是说,在写go代码时,无需过多的关注排列结构和对齐等,最后使用gofmt格式化完就都变成统一风格了。
例如,下边的原始代码:
type T struct { name string // name of the object value int // its value }使用gofmt格式化完之后:
type T struct { name string // name of the object value int // its value }注意字段及注释的对齐方式变化。另外,标准包中的所有Go代码都已使用gofmt格式化过了。
gofmt是一个单独的命令,用来格式化Go的源代码。它用制表符(tab)进行缩进,空格进行对齐。对齐假定编辑器使用的是等宽字体。如果没有显式指定路径,它将处理标准输入;给定一个文件,它将处理该文件;给定一个文件夹,他将递归处理该文件夹下的所有.go文件(隐藏文件除外)。默认情况下,gofmt会将重新格式化好的代码打印至标准输出(而不是直接更新源文件)。 用法如下: gofmt [flags] [path ...]
flags如下: -d 重新格式化后的代码不再打印至标准输出。如果文件代码格式与gofmt不一致,打印区别至标准输出(该标识类似git的diff命令)。 -e 打印所有的错误(包括假的)。 -l 重新格式化后的代码不再打印至标准输出。如果文件代码格式与gofmt不一致,打印该文件名至标准输出。 -r rule 重新格式化源文件前应用指定的重写规则。 -s 在应用规则后(如果有的话),尝试简化代码。 -w 重新格式化后的代码不再打印至标准输出。如果文件代码格式与gofmt不一致,使用gofmt的版本进行重写。重写过程中如果出现错误,原始文件将使用自动备份进行还原。
调试支持: -cpuprofile filename 将cpuprofile写入到指定的文件。
注意:-r标识指定的重写规则必须是一个字符串形式: pattern -> replacement
pattern和replacement部分必须是一个有效的Go表达式。在pattern中,单字符小写标识符用作匹配任意子表达式的通配符,这些表达式将替换为replacement中相同的标识符。
当gofmt从标准输入读取时,即接受一个完整的Go程序,也可以是一个程序片段。程序片段必须是语法上有效的声明列表,语句列表或表达式。格式化这种片段时,gofmt会保留前导缩进和前后的空格,以便Go程序的各个部分可以通过gofmt来格式化。
假设源文件(hello.go)内容如下:
package main import "fmt" func main() { x := 2 y := 3// 该行未对齐 str := "Hello Golang~" var greeting string greeting = (str)// 本行含有不必要的括号 fmt.Println(greeting) fmt.Println("x*y =", ((x) * y))// 本行含有不必要的括号 s := []int{1, 3, 5, 6, 7}// 切片 start := 2 sub := s[start:len(s)]// 本行可以优化切片s上界 fmt.Println(s) fmt.Println(sub) }1.检查文件中不必要的括号(如果有,则输出文件名):
gofmt -r '(a) -> a' -l *.go将会输出hello.go
2.移除括号:
gofmt -r '(a) -> a' -w *.go源文件将变成如下格式:
package main import "fmt" func main() { x := 2 y := 3 // 该行未对齐 str := "Hello Golang~" var greeting string greeting = str // 本行含有不必要的括号 fmt.Println(greeting) fmt.Println("x*y =", x*y) // 本行含有不必要的括号 s := []int{1, 3, 5, 6, 7} // 切片 start := 2 sub := s[start:len(s)] // 本行可以优化切片s上界 fmt.Println(s) fmt.Println(sub) }注意看带注释的行发生的变化。
3.当前目录下,从显式切片上界转换为隐式切片上界:
gofmt -r 'α[β:len(α)] -> α[β:]' -w ./源文件第22行将变成如下:
sub := s[start:] // 本行可以优化切片上限使用-s调用gofmt时,将尽可能进行以下转换:
以下数组,切片或映射的复合字面量形式: []T{T{}, T{}} 将被简化为: []T{{}, {}} 以下切片表达式形式: s[a:len(s)] 将被简化为: s[a:] 以下range形式: for x, _ = range v {...} 将被简化为: for x = range v {...} 以下range形式: for _ = range v {...} 将被简化为: for range v {...}注意:这些改变可能与早期版本的Go不兼容。另外,官方文档中指出: -r标识性能有点慢; -w如果失败,还原后的源文件可能会丢失某些文件属性。
go的子命令fmt也能格式化代码,实际上该命令就是执行的gofmt -l -w命令。执行完毕会打印出被修改文件的名称。
用法如下: go fmt [-n] [-x] [packages]
flags: -n 打印出将要执行的命令,但不执行。 -x 执行并打印执行的命令。
该命令也支持指定单个go文件
关于格式化的其他一些细节:
缩进 Go使用制表符(tab)进行缩进,只在必要时使用空格。单行长度 Go对单行代码长度没有限制,如果感觉一行写的实在太长了,可以换行并再增加一个tab。括号 相比C和Java,Go中很少需要括号,控制结构中(if、for 和 switch)不需要括号。此外,运算符的优先级层次更加清晰,例如以下表达式: x<<8 + y<<16 表达的意思正如空格所暗示的一样。参考: https://golang.org/doc/effective_go.html#formatting https://golang.org/cmd/gofmt/ https://golang.org/cmd/go/#hdr-Gofmt__reformat__package_sources