写在最前:包package
- 一个文件夹下面只能有一个包,同样一个包的文件不能在多个文件夹下
- 包名可以不和文件夹的名字一样,包名不能包含
-符号
如果想在一个包中已用另一个包中的标识符(变量、常量、类型、函数),该标识符必须是对外可见的(public)。在Go语言中将标识符的首字母大写就可以让标识符对外可见了
package pkg
import "fmt"
var a = 100 // 首字母小写,外部包不可见,只能在当前包内使用
const Mode = 1 // 首字母大写,外部包课件,可在其他包中使用
type person struct { // 首字母不可见
name string
}
包内的init函数:无参数+无返回值
在GO语言中,导入包后会自动触发包内部的init()函数调用
init函数在程序运行时自动被调用执行,不能在代码中主动调用它
init函数的执行时机:全局声明 ==> init() ==> main()
1. fmt包
1.1. fmt 格式化输出
1.1.1. 打印输出: Print/Println/Printf
// 直接输出内容
func Print(a ...interface{}) (n int, err error)
// 直接输出内容,添加一个换行符
func Println(a ...interface{}) (n int, err error)
// 格式化输出
func Printf(format string, a ...interface{}) (n int, err error)
1.1.2. 输出到文件: Fprint/Fprintln/Fprintf
将内容输出到一个io.Writer接口类型的变量w中 ==> 通常用于向文件中写入内容
func Fprint(w io.Writer, a ...interface{}) (n int, err error)
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
func Fprintln(w io.Writer, a ...interface{}) (n int, err error)
// 向标准输出写入内容
fmt.Fprintln(os.Stdout, "向标准输出写入内容")
fileObj, err := os.OpenFile("./xx.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("打开文件出错,err:", err)
return
}
name := "沙河小王子"
// 向打开的文件句柄中写入内容
fmt.Fprintf(fileObj, "往文件中写如信息:%s", name)
1.1.3. 格式化成字符串: Sprint/Sprintln/Sprintf
func Sprint(a ...interface{}) string
func Sprintf(format string, a ...interface{}) string
func Sprintln(a ...interface{}) string
s1 := fmt.Sprint("沙河小王子")
name := "沙河小王子"
age := 18
s2 := fmt.Sprintf("name:%s,age:%d", name, age) // 格式化保存到字符串
s3 := fmt.Sprintln("沙河小王子")
fmt.Println(s1, s2, s3)
1.1.4. 格式化占位符
占位符 | 说明 |
---|---|
%v | 值的默认格式表示 |
%+v | 类似%v,但输出结构体时会添加字段名 |
%#v | 值的Go语法表示 |
%T | 打印值的类型 |
%% | 百分号 |
fmt.Printf("%v\n", 100) // 100
fmt.Printf("%v\n", false) // false
o := struct{ name string }{"小王子"}
fmt.Printf("%v\n", o) // {小王子}
fmt.Printf("%#v\n", o) // 转换为字符串: struct { name string }{name:"小王子"}
fmt.Printf("%T\n", o) // 类型: struct { name string }
fmt.Printf("%%100\n") // %100
1.2. fmt 获取输入
1.2.1. Scan/Scanln/Scanf
// 从“标准输入”中扫描用户输入的数据,将以“空白符”分隔的数据分别存入指定的参数
func Scan(a ...interface{}) (n int, err error)
// 类似于Scan,它在遇到换行符\n时才停止扫描 (最后一个数据后面必须有换行或者到达结束位置)
func Scanln(a ...interface{}) (n int, err error)
// 必须以format指定格式作为输入
func Scanf(format string, a ...interface{}) (n int, err error)
1.2.2. Fscan/Fscanln/Fscanf
从io.Reader中获取数据
func Fscan(r io.Reader, a ...interface{}) (n int, err error)
func Fscanln(r io.Reader, a ...interface{}) (n int, err error)
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)
1.2.3. Sscan/Sscanln/Sscanf
这几个函数功能分别类似于fmt.Scan
、fmt.Scanf
、fmt.Scanln
三个函数,只不过它们不是从标准输入中读取数据而是从指定字符串中读取数据。
func Sscan(str string, a ...interface{}) (n int, err error)
func Sscanln(str string, a ...interface{}) (n int, err error)
func Sscanf(str string, format string, a ...interface{}) (n int, err error)
1.2.4. bufio.NewReader
有时候我们想完整获取输入的内容,而输入的内容可能包含空格,这种情况下可以使用bufio
包来实现:
func bufioDemo() {
reader := bufio.NewReader(os.Stdin) // 从标准输入生成读对象
fmt.Print("请输入内容:")
text, _ := reader.ReadString('\n') // 获取输入,直到遇到\n才终止获取
text = strings.TrimSpace(text) // 移除string两端的空格
fmt.Printf("%#v\n", text)
}
2. time包—定时器
定时器本质上是一个chan
2.1. 单次触发: NewTimer/After
package main
import (
"fmt"
"time"
)
func main() {
// 用sleep实现定时器
fmt.Println("[1] ", time.Now())
time.Sleep(time.Second)
fmt.Println("[2] ", time.Now())
// 用timer实现定时器
timer := time.NewTimer(time.Second)
fmt.Println("[3] ", <-timer.C)
// 用after实现定时器
fmt.Println("[4] ", <-time.After(time.Second))
}
2.2. 时间间隔触发: time.Tick
案例:每间隔1s,定时器就会触发一次
// 方式1
func tickDemo() {
ticker := time.Tick(time.Second) // 定义一个1s间隔的定时器
for i := range ticker { // for循环从通道ticker中读取
fmt.Println(i)
}
}
// 方式2
func main() {
tiker := time.NewTicker(time.Second) // 定义一个1s间隔的定时器
for i := 0; i < 3; i++ {
fmt.Println(<-tiker.C)
}
}
2.3. Reset/Stop
重置定时器: timer.Reset(d Duration)
停止定时器:timer.Stop()
2.4. 关于time的其他API
(1) 时间获取
-
获取当前时间:time.Now()
package main import ( "fmt" "time" ) func main() { now := time.Now() //获取当前时间 fmt.Printf("current time:%v\n", now) // // current time:2019-12-12 12:33:19.4712277 +0800 CST m=+0.006980401 year := now.Year() //年 month := now.Month() //月 day := now.Day() //日 hour := now.Hour() //小时 minute := now.Minute() //分钟 second := now.Second() //秒 fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second) // // 2019-12-12 12:33:19 }
-
获取时间戳
- 时间戳是自 1970 年 1 月 1 日(08:00:00GMT)至当前时间的总毫秒数,它也被称为 Unix 时间戳(UnixTimestamp)
package main import ( "fmt" "time" ) func main() { now := time.Now() //获取当前时间 timestamp := now.Unix() //时间戳 timeObj := time.Unix(timestamp, 0) //将时间戳转为时间格式 fmt.Println(timeObj) // 2019-12-12 13:24:09 +0800 CST year := timeObj.Year() //年 month := timeObj.Month() //月 day := timeObj.Day() //日 hour := timeObj.Hour() //小时 minute := timeObj.Minute() //分钟 second := timeObj.Second() //秒 fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second) // 2019-12-12 13:24:09 }
(2) 时间操作函数
func (t Time) Add(d Duration) Time
==> 返回时间点 t + 时间间隔 d 的值func (t Time) Sub(u Time) Duration
==> 求两个时间之间的差值func (t Time) Equal(u Time) bool
==> 判断两个时间是否相同- Before / After
3. flag包: 命令行参数解析
3.1. os.Args
如果你只是简单的想要获取命令行参数,可以像下面的代码示例一样使用os.Args
来获取命令行参数。
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) > 0 { //os.Args是一个[]string
for index, arg := range os.Args {
fmt.Printf("args[%d]=%v\n", index, arg)
}
}
}
执行结果,输入test.exe a b c d,输出见下
C:\Users\Sangfor\go\src\awesomeProject>test.exe a b c d
args[0]=test.exe
args[1]=a
args[2]=b
args[3]=c
args[4]=d
3.2. flag包: 可配参数案例
package main
import (
"flag"
"fmt"
"time"
)
func main() {
//定义变量用于接收flag
var name string
var age int
var married bool
var delay time.Duration
// flag.[Type]Var(Type指针, flag名, 默认值, 帮助信息) : 获取输入的值
// &name变量(接收数据) "name"(-name) "张三"(缺省默认值) "姓名"(描述信息)
flag.StringVar(&name, "name", "张三", "姓名")
flag.IntVar(&age, "age", 18, "年龄")
flag.BoolVar(&married, "married", false, "婚否")
flag.DurationVar(&delay, "d", 0, "延迟的时间间隔")
/* 解析命令行参数
-flag xxx 使用空格,一个-符号
--flag xxx 使用空格,两个-符号
-flag=xxx 使用等号,一个-符号
--flag=xxx 使用等号,两个-符号
*/
flag.Parse()
fmt.Println(name, age, married, delay) // 沙河娜扎 28 false 1h30m0s
fmt.Println(flag.Args()) //返回命令行参数后的其他参数 []
fmt.Println(flag.NArg()) //返回命令行参数后的其他参数个数 0
fmt.Println(flag.NFlag()) //返回使用的命令行参数个数 4
}
/* 执行效果
$ test.exe -help
Usage of ./flag_demo:
-age int
年龄 (default 18)
-d duration
时间间隔
-married
婚否
-name string
姓名 (default "张三")
> test.exe -married=false -d=1h30m --age 28 -name 沙河娜扎
沙河娜扎 28 false 1h30m0s
[]
0
4
*/
4. sort包:排序
-
前言
① sort 包内置的提供了根据一些排序函数来对任何序列排序的功能。它的设计非常独到。在很多语言中,排序算法都是和序列数据类型关联,同时排序函数和具体类型元素关联。
② 相比之下,Go语言的 sort.Sort 函数不会对具体的序列和它的元素做任何假设。相反,它使用了一个接口类型 sort.Interface 来指定通用的排序算法和可能被排序到的序列类型之间的约定。这个接口的实现由序列的具体表示和它希望排序的元素决定,序列的表示经常是一个切片。
-
一个内置的排序算法需要实现3个接口
package sort type Interface interface { Len() int // 获取元素长度 Less(i, j int) bool // 比较函数 Swap(i, j int) // 交换元素的方式 }
为了对序列排序,需要:①先定义一个类型,该类型实现了这3个方法;②定义该类型的实例对象obj,执行sort.Sort(obj)进行排序
package main import ( "fmt" "sort" ) // 将[]string定义为MyStringList类型 type MyStringList []string // 实现sort.Interface接口的3个方法 func (m MyStringList) Len() int { return len(m) } func (m MyStringList) Less(i, j int) bool { return m[i] < m[j] } func (m MyStringList) Swap(i, j int) { m[i], m[j] = m[j], m[i] } func main() { // 准备一个内容被打乱顺序的字符串切片 names := MyStringList{ "3. Triple Kill", "5. Penta Kill", "2. Double Kill", "4. Quadra Kill", "1. First Blood", } // 使用sort包的Sort函数,将names(MyStringList类型)进行排序 sort.Sort(names) // 遍历打印结果 for _, v := range names { fmt.Printf("%s\n", v) } } /* 1. First Blood 2. Double Kill 3. Triple Kill 4. Quadra Kill 5. Penta Kill */
-
常见类型的便捷排序
GO中的sort包,提供了定制化的排序包,根据切片slice中的类型,如下
① slice元素类型:string
-
方式1 ==> sort.Strings(a [] string)
names := []string{ // 普通的字符串切片 "3. Triple Kill", "5. Penta Kill", "2. Double Kill", "4. Quadra Kill", "1. First Blood", } sort.Strings(names) // 使用 sort.Strings 直接对字符串切片进行排序。 for _, v := range names { fmt.Printf("%s\n", v) }
-
方式2 ==> sort.StringSlice
sort 包中有一个 StringSlice 类型,定义如下:
type StringSlice []string func (p StringSlice) Len() int { return len(p) } func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] } func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Sort is a convenience method. func (p StringSlice) Sort() { Sort(p) }
使用案例
names := sort.StringSlice{ // 使用sort.StringSlice创建结构体 "3. Triple Kill", "5. Penta Kill", "2. Double Kill", "4. Quadra Kill", "1. First Blood", } sort.Sort(names)
② slice元素类型:整形 ==> sort.Ints(a []int) sort.Float64s(a []float64)
-
-
自定义结构体的排序 ==> sort.Slice
从 Go 1.8 开始,Go语言在 sort 包中提供了 sort.Slice() 函数进行更为简便的排序方法。使用 sort.Slice() 不仅可以完成结构体切片排序,还可以对各种切片类型进行自定义排序。
sort.Slice() 函数只要求传入需要排序的数据,以及一个排序时对元素的回调函数,类型为 func(i,j int)bool,sort.Slice() 函数的定义如下:
/* * @param [in] slice 需要排序的切片 * @param [in] less 回调函数:比较大小 */ func Slice(slice interface{}, less func(i, j int) bool)
示例代码:sort.Slice
package main import ( "fmt" "sort" ) type HeroKind int const ( None = iota Tank Assassin Mage ) type Hero struct { Name string Kind HeroKind } func main() { /* heros := Heros{ &Hero{"吕布", Tank}, &Hero{"诸葛亮", Mage}, } */ heros := []*Hero{ // 准备英雄列表 {"吕布", Tank}, {"诸葛亮", Mage}, } sort.Slice(heros, func(i, j int) bool { if heros[i].Kind != heros[j].Kind { return heros[i].Kind < heros[j].Kind } return heros[i].Name < heros[j].Name }) for _, v := range heros { fmt.Printf("%+v\n", v) } }