文件操作
文件的打开:
使用 os.Open()
函数可以打开一个文件返回一个*File
和一个err
。相对的,使用 [文件对象].Close()
可以关闭对应文件
注:为防止忘记关闭文件,一般我们会加上 defer 推迟语句保证执行后关闭
读取文件:
1、使用 read 方法
Read方法定义如下:
1 | func (f *File) Read(b []byte) (n int, err error) |
它接收一个字节切片,返回读取的字节数和可能的具体错误,读到文件末尾时会返回 0
和 io.EOF
。
需要传入的参数是一个字节切片
1 | func read1() { |
2、使用 bufio 方法
bufio是在file的基础上封装了一层API,支持更多的功能。
1 | package main |
3、使用 ioutil 直接读取整个文件
(很消耗内存,大文件读取不推荐)
1 | func read3(){ |
文件写入操作:
首先我们需要认识 os.OpenFile
这个新函数,它的定义是:
1 | func OpenFile(name string, flag int, perm FileMode) (*File, error) |
传入参数中,name 指的是要打开的文件名字,flag 指打开文件的模式,主要有以下几种:
模式 | 含义 |
---|---|
os.O_WRONLY |
只写 |
os.O_CREATE |
创建文件 |
os.O_RDONLY |
只读 |
os.O_RDWR |
读写 |
os.O_TRUNC |
清空 |
os.O_APPEND |
追加 |
当需要进行多种操作时,只需要用 “|” 连接(毕竟都是 int 值)
这个 perm 倒是看上去莫名其妙的,实际上这里装的是一个关系权限的八进制数,可以通过这个函数获取结果:
1 | n := os.FileMode(0666).String() |
得出 -rw-rw-rw-
的答案:
1 | 一般文件属性标识如下: |
然后和读取相对的,写入也有三种不同的方法:
1、使用 Write 和 WriteString
1 | func main() { |
2、bufio.NewWriter
1 | func main() { |
这样一来,会循环写入十次 “hello world” 而只需要一次 Flush
3、ioutil.WriteFile
1 | func main() { |
这个用法自带清场效果,慎用
fmt
有些类似于 iostream 这些输入输出流的库,实现了类似C语言 printf 和 scanf 的格式化 I/O。主要分为向外输出内容和获取输入内容两大部分。
Print 系列
相关定义如下:
1 | func Print(a ...interface{}) (n int, err error) |
print 直接输出内容,Printf 支持格式化输出字符串(%d,%s…)与 C 中用法类似,而 Println 和 Print 相似,只不过输出后会自动换行。
可以看到他们都可以自定义接口,详见之前的 Go 语言入门。
Fprint
定义如下:
1 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) |
Fprint
系列函数会将内容输出到一个 io.Writer
接口类型的变量 w
中,我们通常用这个函数往文件中写入内容。
1 | // 向标准输出写入内容 |
只要满足 io.Writer
接口类型,都可以写入
Sprint
定义如下:
1 | func Sprint(a ...interface{}) string |
这个函数主要是用来转字符串的,它可以将传入数据转化成字符串,传入数据格式和 Print 类是一样的。
1 | 比如 str:=Sprint("hello world!") 此时 str 中存储的就是 “hello world!” |
Errorf
Errorf
函数根据format参数生成格式化字符串并返回一个包含该字符串的错误。
1 | func Errorf(format string, a ...interface{}) error |
通常使用这种方式来自定义错误类型,例如:
1 | err := fmt.Errorf("这是一个错误") |
Go1.13版本为fmt.Errorf
函数新加了一个%w
占位符用来生成一个可以包裹Error的Wrapping Error。
1 | e := errors.New("原始错误e") |
格式化占位符
*printf
系列函数都支持format格式化参数,在这里我们按照占位符将被替换的变量类型划分,方便查询和记忆。
通用占位符
占位符 说明 %v 值的默认格式表示 %+v 类似%v,但输出结构体时会添加字段名 %#v 值的Go语法表示 %T 打印值的类型 %% 百分号 示例代码如下:
1
2
3
4
5
6
7 fmt.Printf("%v\n", 100)
fmt.Printf("%v\n", false)
o := struct{ name string }{"小王子"}
fmt.Printf("%v\n", o)
fmt.Printf("%#v\n", o)
fmt.Printf("%T\n", o)
fmt.Printf("100%%\n")输出结果如下:
1
2
3
4
5
6 100
false
{小王子}
struct { name string }{name:"小王子"}
struct { name string }
100%
布尔型
占位符 说明 %t true或false
整型
占位符 说明 %b 表示为二进制 %c 该值对应的unicode码值 %d 表示为十进制 %o 表示为八进制 %x 表示为十六进制,使用a-f %X 表示为十六进制,使用A-F %U 表示为Unicode格式:U+1234,等价于”U+%04X” %q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
进制转换倒是挺方便
浮点数与复数
占位符 说明 %b 无小数部分、二进制指数的科学计数法,如-123456p-78 %e 科学计数法,如-1234.456e+78 %E 科学计数法,如-1234.456E+78 %f 有小数部分但无指数部分,如123.456 %F 等价于%f %g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出) %G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出) 示例代码如下:
1
2
3
4
5
6
7 f := 12.34
fmt.Printf("%b\n", f)
fmt.Printf("%e\n", f)
fmt.Printf("%E\n", f)
fmt.Printf("%f\n", f)
fmt.Printf("%g\n", f)
fmt.Printf("%G\n", f)输出结果如下:
1
2
3
4
5
6 6946802425218990p-49
1.234000e+01
1.234000E+01
12.340000
12.34
12.34
字符串和[]byte
占位符 说明 %s 直接输出字符串或者[]byte %q 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示 %x 每个字节用两字符十六进制数表示(使用a-f %X 每个字节用两字符十六进制数表示(使用A-F)
指针
占位符 说明 %p 表示为十六进制,并加上前导的0x 示例代码如下:
1
2
3 a := 10
fmt.Printf("%p\n", &a)
fmt.Printf("%#p\n", &a)输出结果如下:
1
2 0xc000094000
c000094000
宽度标识符
宽度通过一个紧跟在百分号后面的十进制数指定,如果未指定宽度,则表示值时除必需之外不作填充。精度通过(可选的)宽度后跟点号后跟的十进制数指定。如果未指定精度,会使用默认精度;如果点号后没有跟数字,表示精度为0。举例如下:
占位符 说明 %f 默认宽度,默认精度 %9f 宽度9,默认精度 %.2f 默认宽度,精度2 %9.2f 宽度9,精度2 %9.f 宽度9,精度0 示例代码如下:
1
2
3
4
5
6 n := 12.34
fmt.Printf("%f\n", n)
fmt.Printf("%9f\n", n)
fmt.Printf("%.2f\n", n)
fmt.Printf("%9.2f\n", n)
fmt.Printf("%9.f\n", n)输出结果如下:
1
2
3
4
5 12.340000
12.340000
12.34
12.34
12
其他flag
占位符 说明 ’+’ 总是输出数值的正负号;对%q(%+q)会生成全部是ASCII字符的输出(通过转义); ’ ‘ 对数值,正数前加空格而负数前加负号;对字符串采用%x或%X时(% x或% X)会给各打印的字节之间加空格 ’-’ 在输出右边填充空白而不是默认的左边(即从默认的右对齐切换为左对齐); ’#’ 八进制数前加0(%#o),十六进制数前加0x(%#x)或0X(%#X),指针去掉前面的0x(%#p)对%q(%#q),对%U(%#U)会输出空格和单引号括起来的go字面值; ‘0’ 使用0而不是空格填充,对于数值类型会把填充的0放在正负号后面; 举个例子:
1
2
3
4
5
6
7
8 s := "小王子"
fmt.Printf("%s\n", s)
fmt.Printf("%5s\n", s)
fmt.Printf("%-5s\n", s)
fmt.Printf("%5.7s\n", s)
fmt.Printf("%-5.7s\n", s)
fmt.Printf("%5.2s\n", s)
fmt.Printf("%05s\n", s)输出结果如下:
1
2
3
4
5
6
7 小王子
小王子
小王子
小王子
小王子
小王
00小王子
获取输入
fmt.Scan
定义如下:
1 | func Scan(a ...interface{}) (n int, err error) |
- Scan从标准输入扫描文本,读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
- 本函数返回成功扫描的数据个数和遇到的任何错误。如果读取的数据个数比提供的参数少,会返回一个错误报告原因。
- 需要使用 ‘&’ 取址
fmt.Scanf
1 | func Scanf(format string, a ...interface{}) (n int, err error) |
由上面的代码看到,string 之前有一个 “format” ,说明需要适当的格式化输入。可类比 Printf
同样,读入时也是按空白符分割。
fmt.Scanln
1 | func Scanln(a ...interface{}) (n int, err error) |
- Scanln类似Scan,它在遇到换行时才停止扫描。最后一个数据后面必须有换行或者到达结束位置。
- 本函数返回成功扫描的数据个数和遇到的任何错误。
bufio.NewReader
之前在读取文件时,用到了 bufio,其实在读取用户输入时,为了获取包含空格的内容,bufio 也十分管用。
1 | func bufioDemo() { |
Fscan系列
这几个函数功能分别类似于fmt.Scan
、fmt.Scanf
、fmt.Scanln
三个函数,只不过它们不是从标准输入中读取数据而是从io.Reader
中读取数据。
1 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) |
Sscan系列
这几个函数功能分别类似于fmt.Scan
、fmt.Scanf
、fmt.Scanln
三个函数,只不过它们不是从标准输入中读取数据而是从指定字符串中读取数据。
1 | func Sscan(str string, a ...interface{}) (n int, err error) |
time 包
时间类型
time.Time 类型表示时间。可以通过 time.Now() 获取当前时间,而具体时间也有相关获取函数:
函数 | 效果 |
---|---|
time.Year() | 获取年 |
time.Month() | 获取月 |
time.Day() | 获取日 |
time.Hour() | 获取时 |
time.Minute() | 获取分 |
time.Second() | 获取秒 |
Go 的时间库算是比较强大了,毕竟甚至可以进行 Tues+1==Wed 这样的运算
时间戳
时间戳是自1970年1月1日(08:00:00GMT)至当前时间的总毫秒数。它也被称为Unix时间戳(UnixTimestamp)。
同样,对应时间类型,只需要 .Unix
获取时间戳或者用 .Unixnano
获取纳秒时间戳即可
值得注意的是,时间戳格式还可以重新换成时间格式:
1 | timeObj := time.Unix(timestamp, 0) //将时间戳转为时间格式 |
这个函数里面的两个参数分别对应时间戳和纳秒时间戳,通常将目标以外的 另一个参数置零
时间间隔
time.Duration
是time
包定义的一个类型,它代表两个时间点之间经过的时间,以纳秒为单位。time.Duration
表示一段时间间隔,可表示的最长时间段大约290年。
time包中定义的时间间隔类型的常量如下:
1 | const ( |
例如:time.Duration
表示1纳秒,time.Second
表示1秒。
时间操作

有时候我们需要进行一些时间操作,这个时候 Go 语言丰富的方法就为我们提供了便捷。
Add
我们在日常的编码过程中可能会遇到要求时间+时间间隔的需求,Go语言的时间对象有提供Add方法如下:
1 | func (t Time) Add(d Duration) Time |
即对任意 Time 类型参数 t ,都可以使用 t.Add(间隔时间参数)
得到增加后的时间数据。
另外,如果是想进行减去时间间隔的运算,只需要将参数 d 设置为对应负数即可。
Sub
求两个时间之间的差值:
1 | func (t Time) Sub(u Time) Duration |
返回一个时间段 **t-u (顺序调换会异号)**。如果结果超出了Duration可以表示的最大值/最小值,将返回最大值/最小值。
Equal
1 | func (t Time) Equal(u Time) bool |
判断两个时间是否相同,会考虑时区的影响,因此 不同时区标准的时间也可以正确比较。本方法和用 t==u
不同,这种方法还会比较地点和时区信息。
Before/After
1 | func (t Time) Before(u Time) bool |
如果t代表的时间点在u之前,返回真;否则返回假。
1 | func (t Time) After(u Time) bool |
如果t代表的时间点在u之后,返回真;否则返回假。
定时器
使用 time.Tick(时间间隔)
来设置定时器,定时器的 本质上是一个通道(channel)。
1 | func tickDemo() { |
不过既然是定时器,配套设施也很多,比如我们的 time.Sleep()
和它的完全体 time.NewTicker()
。
time.Sleep() 主要提供一个进程休眠的过程,而 Tick()本身就 只是一个相对残缺的部分,它只传回一个 channel,而 time.NewTicker() 则提供了包含关闭 Ticker 的方法,在需要回收的情况下更合适。
1 | func main() { |
时间格式化
Time 类型拥有一个 Format
方法来进行格式化,然而 Go 语言毕竟是人家大公司的作品,连个格式化都要搞一手致敬——它的格式化模板不是常见的 Y-m-d H:M:S
,而是使用Go的诞生时间2006年1月2号15点04分。
注:想格式化为12小时类型的化,还需指定
PM
。
1 | func formatDemo() { |
解析字符串格式的时间
1 | now := time.Now() |
log
Go语言内置的log
包实现了简单的日志服务。本文介绍了标准库log
的基本使用。
使用Logger
log 包定义了 Logger 类型,该类型提供了一些格式化输出的方法。本包也提供了一个预定义的“标准” logger,可以通过调用函数
Print系列
(Print|Printf|Println)、Fatal系列
(Fatal|Fatalf|Fatalln)、和Panic系列
(Panic|Panicf|Panicln)来使用,比自行创建一个 logger 对象更容易使用。
1 | func main() { |
logger 会打印每条日志信息的日期、时间,默认输出到系统的标准错误。Fatal 系列函数会在写入日志信息后调用 os.Exit(1)。Panic 系列函数会在写入日志信息后 panic。
os.Exit(1):指非正常运行导致退出程序
os.Exit(0):正常运行导致退出程序
fatal 以及 panic 触发后,后续语句都不会执行。
配置logger
默认情况下只会让 log 返回一部分时间信息,要是我们想获取更多信息该怎么办呢?
log
标准库中的 Flags
函数会返回标准logger的输出配置,而 SetFlags
函数用来设置标准 logger 的输出配置。
1 | func Flags() int |
flag选项
通过 flag 可以选择输出配置,他们是一系列定义好的常量
1 | const ( |
使用 log.SetFlags() 设置标准logger的输出
1 | func main() { |
配置日志前缀
类比我们的 Flag,前缀使用 Prefix 表示,log
标准库中还提供了关于日志信息前缀的两个方法:
1 | func Prefix() string//查看 logger 的输出前缀 |
这样我们就能够在代码中为我们的日志信息添加指定的前缀,方便之后对日志信息进行检索和处理。
配置日志输出位置
SetOutput
函数用来设置标准 logger 的输出目的地,默认是标准错误输出。
1 | func SetOutput(w io.Writer) |
看见 io.Writer 就知道可能要用 os.OpenFile 了,这里设定是将日志输出到对应文件(不过你用 os.stdout 也没毛病)
如果你要使用标准的logger,我们通常会把配置操作写到init
函数中。
1 | func init() { |
创建logger
log
标准库中还提供了一个创建新 logger 对象的构造函数– New
,支持我们创建自己的 logger 示例。New
函数的签名如下:
1 | func New(out io.Writer, prefix string, flag int) *Logger |
New 创建一个 Logger 对象。其中,参数 out 设置日志信息写入的目的地。参数 prefix 会添加到生成的每一条日志前面。参数 flag 定义日志的属性(时间、文件等等)。
举个例子:
1 | func main() { |
将上面的代码编译执行之后,得到结果如下:
1 | <New>2017/06/19 14:06:51 main.go:34: 这是自定义的 logger 记录的日志。 |
net/http
HTTP协议
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。
HTTP客户端
基本的HTTP/HTTPS请求
Get、Head、Post和PostForm函数发出HTTP/HTTPS请求。
1 | resp, err := http.Get("http://example.com/") |
程序在使用完response后必须关闭回复的主体。
1 | resp, err := http.Get("http://example.com/") |