本章对于golang标准库中常用包的使用进行说明。
flag包实现了命令行参数的解析。通过:flag.String(), Bool(), Int()
定义不同类型的命令行参数。
示例:
import "flag"
var nFlag = flag.Int("n", 1234, "help message for flag n")
定义了int类型的参数,需要注意的是返回了int类型指针,方法定义为:
func Int(name string, value int, usage string) *int
参数:
- name,flag名称;
- value,默认值;
- usage,使用提示。
或者通过Var方法将参数绑定到某个变量:
var flagvar int
func init() {
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}
定义了flag之后,调用flag.Parse()
解析命令行参数到定义的flag中。
示例程序:
package main
import (
"flag"
"fmt"
)
func main() {
wordPtr := flag.String("word", "foo", "a string")
numberPtr := flag.Int("numb", 10, "an int")
var svar string
flag.StringVar(&svar, "svar", "bar", "a string var")
flag.Parse()
fmt.Println("word", *wordPtr)
fmt.Println("numb", *numberPtr)
fmt.Println("svar", svar)
}
JSON是对JavaScript中各种类型的值——字符串、数字、布尔值和对象——Unicode本文编码。
Go语言标准库中encoding/json提供了对json格式数据的编解码处理。
配置文件基于json格式,读取配置文件格式到结构体中,示例程序:
package main
import (
"fmt"
"encoding/json"
_ "os"
)
type MyStruct struct {
Name string
Age int
}
func main() {
stud := MyStruct {
Name: "tony",
Age: 13}
data, err := json.Marshal(stud)
if err != nil {
fmt.Println("json marshal failed:%s", err)
}
fmt.Println(string(data)
}
结构体输出结果为:
文件内容如下:
{
"structlist" :[
{
"Name": "tony",
"Age": 27
},
{
"Name": "tom",
"Age": 22
}
]
}
定义对应的结构体:
type MyStructList struct {
StructList []MyStruct `json:"structlist"`
}
type MyStruct struct {
Name string `json:"Name"`
Age int `json:"Age"`
}
读取json文件后解码:
//读取json file
filepath := "conf.json"
jsonfile, err := os.Open(filepath)
if err != nil {
fmt.Println("open error.")
os.Exit(1)
}
defer jsonfile.Close()
//读取json文件
byteBuffer, err := ioutil.ReadAll(jsonfile)
fmt.Println(string(byteBuffer))
var mylists MyStructList
unerr := json.Unmarshal(byteBuffer, &mylists)
if unerr != nil {
fmt.Println(unerr)
os.Exit(1)
}
//输出json信息
listlen := len(mylists.StructList)
fmt.Println("the json list len is:", listlen)
for i := 0; i < listlen; i++ {
fmt.Println("Name: ", mylists.StructList[i].Name)
fmt.Println("Age: ", mylists.StructList[i].Age)
fmt.Println("---")
}
对于文件创建、打开、关闭、写入、读取等基本操作进行描述。
golang中文件的创建可以通过create
函数:
func Create(name string) (*File, error)
重建或者截断,如果文件存在则截断,不存在则创建文件,创建的模式为0666.
或者通过open
函数:
func OpenFile(name string, flag int, perm FileMode) (*File, error)
默认flag是O_RDONLY 。如果文件不存在则创建,传入 O_CREATE flag 。
Flag列表如下:
const (
// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
O_RDONLY int = syscall.O_RDONLY // open the file read-only.
O_WRONLY int = syscall.O_WRONLY // open the file write-only.
O_RDWR int = syscall.O_RDWR // open the file read-write.
// The remaining values may be or'ed in to control behavior.
O_APPEND int = syscall.O_APPEND // append data to the file when writing.
O_CREATE int = syscall.O_CREAT // create a new file if none exists.
O_EXCL int = syscall.O_EXCL // used with O_CREATE, file must not exist.
O_SYNC int = syscall.O_SYNC // open for synchronous I/O.
O_TRUNC int = syscall.O_TRUNC // truncate regular writable file when opened.
)
mode类型如下:
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir = fs.ModeDir // d: is a directory
ModeAppend = fs.ModeAppend // a: append-only
ModeExclusive = fs.ModeExclusive // l: exclusive use
ModeTemporary = fs.ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink = fs.ModeSymlink // L: symbolic link
ModeDevice = fs.ModeDevice // D: device file
ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO)
ModeSocket = fs.ModeSocket // S: Unix domain socket
ModeSetuid = fs.ModeSetuid // u: setuid
ModeSetgid = fs.ModeSetgid // g: setgid
ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky = fs.ModeSticky // t: sticky
ModeIrregular = fs.ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = fs.ModeType
ModePerm = fs.ModePerm // Unix permission bits, 0o777
)
package main
import (
"os"
"log"
"fmt"
)
func main() {
filepath := "test.txt"
newFile, err := os.Create(filepath)
if err != nil {
log.Fatal(err)
}
//write string
newFile.Write([]byte("hello world"))
newFile.Close()
//open and read file
bytebuffer := make([]byte, 16)
newFile2, err := os.Open(filepath)
nbytes, err:= newFile2.Read(bytebuffer)
fmt.Printf("read len:%d str is:%s", nbytes, string(bytebuffer))
defer newFile2.Close()
}
Go’s encoding/Binary package
Go语言encoding/Binary数据包用于将数值编解码为二进制数据。可以通过多种方法将固定长度类型的数值进行编解码。
并提供了大端、小端处理接口:binary.LittleEndian and binary.BigEndian
。
按照不同的整数类型Uint16\Uint32
等进行编解码:
buf := make([]byte, 10)
//ts := uint32(time.Now().Unix())
binary.BigEndian.PutUint16(buf[0:], 0x0101)
binary.BigEndian.PutUint16(buf[2:], 0x0201)
binary.BigEndian.PutUint32(buf[4:], 0x0301)
binary.BigEndian.PutUint16(buf[8:], 0x0401)
fmt.Printf("%x\n", buf)
sensorID := binary.BigEndian.Uint16(buf[0:])
locID := binary.BigEndian.Uint16(buf[2:])
tstamp := binary.BigEndian.Uint32(buf[4:])
temp := binary.BigEndian.Uint16(buf[8:])
fmt.Printf("sid: %0#x, locID %0#x ts: %0#x, temp:%d\n", sensorID, locID, tstamp, temp)
binary.BigEndian.PutUint16
根据整数类型确定字节数组中的大小- 设置BigEndian或者LittleEndian。
上面的示例中,逐个字段对数据包进行编码和解码,对于具有多个字段的较大数据包可能容易出错。可以通过IO接口中的io.Reader
and io.Writer
编解码数据。
(1)函数binary.Write(w io.Writer, o ByteOrder, data interface{})
上面的示例中定义数据包:
type packet struct {
Sensid uint32
Locid uint16
Tstamp uint32
Temp int16
}
func main() {
dataIn := packet {
Sensid: 1, Locid: 1233, Tstamp: 123452123, Temp: 12,
}
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.BigEndian, dataIn)
if err != nil {
fmt.Println(err)
return
}
//Buffer长度
fmt.Printf("%d\n", buf.Len())
}
需要注意的是结构体仅包含期望顺序的数字类型字段。 数据必须是固定长度的。
如果包含了字符串类型,报错:binary.Write: invalid type main.packet
.
根据上面的约束,可以将字段为固定长度的结构体写入到buf中。假设结构体为:
type MsgHeader struct {
MsgType [4]byte
SendingTtime uint64
MsgSeq uint64
BodyLength uint32
}
type MsgTail struct {
CheckSum uint32
}
//登录消息
type LoginMsg struct {
MsgHeader
SenderCompID [32]byte
TargetCompID [32]byte
HeartBtInt uint16
AppVerID [8]byte
MsgTail
}
初始化结构体然后写入到buf中:
//初始化结构体消息
func initMsg(loginMsg *LoginMsg) {
loginMsg.CheckSum = 512
loginMsg.SendingTtime = 1
}
func main() {
loginMsg := &LoginMsg{}
initMsg(loginMsg)
buf := new(bytes.Buffer)
binary.Write(buf, binary.BigEndian, loginMsg)
fmt.Println("the binary buf size:", buf.Len())
fmt.Printf("%x", buf)
}
消息体最后的字段CheckSum
,赋值512,十六进制表示为:0x200。运行程序输出buf中的字节内容:
可以看到buf中最后的的字节位0200(大端方式)。如果按照小端方式写入:
binary.Write(buf, binary.LittleEndian, loginMsg)
运行程序输出buf中的字节内容:
最后的序列为:0x00020000,最先读到的是低字节位,则表示的值为:0x0200。
上述的列子中,通过write函数向buffer中写入数据。对于字节流可以通过Read函数读取字节到指定的数据类型。
假设结构体为:
type data struct {
A uint32
B uint16
Mytype [3]byte
}
一共9个字节的长度。假设对应的字节流为:
b := []byte{0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x01, 0x02, 0x03}
可以通过binary包中的Read方法读取字节流中的数据,填充结构体中的字段值。
buf := bytes.NewReader(b)
fmt.Println("the reader len is:", buf.Len())
var mydata data
//直接读取到结构体中
err := binary.Read(buf, binary.BigEndian, &mydata)
if err != nil {
fmt.Println("read data structure err")
}
fmt.Printf("%x\n", mydata.A)
fmt.Printf("%x\n", mydata.B)
fmt.Printf("%x\n", mydata.Mytype)
fmt.Println("the reader len is:", buf.Len())
运行程序输出结果:
对于B字段,数据类型为uint16,字节序列数据为0x02, 0x03,按照大端格式读取,得到203。
另外,需要注意的是,如果结构体中变量为小写字母开头,对外面的包不可见,会报错:
panic: SetUint using value obtained using unexported field。
包 bufio 实现缓冲I/O。它包装了一个 io.Reader 或 io.Writer 对象,创建另一个对象(Reader 或 Writer),该对象也实现了该接口,但为文本 I/O 提供了缓冲和一些帮助。
通过NewReader或者NewReaderSize实例化bufio对象。
通过timer定时器,可以在将来某个时间点或者某个时间间隔内重复执行Go代码。Go内建了timer和ticker特性。
timer代表了未来的某个事件,告诉定时器需要等待多久,它提供了一个channel,到时会收到通知。可以放到for循环中定时执行某个函数。
func ExecFunc() {
fmt.Println("execute function")
}
func main() {
for {
fmt.Println("create timer")
timer1 := time.NewTimer(2 * time.Second)
<-timer1.C
fmt.Println("timer 1fired")
ExecFunc()
}
}
定时间隔执行函数。可以通过函数返回值决定是否需要退出。
上一小结中timer主要是未来执行一次。而ticker是用于定时间隔执行。
func main() {
ticker := time.NewTicker(1 * time.Second)
done := make(chan bool)
go func() {
for {
select {
case <-done:
return
case t := <-ticker.C:
fmt.Println("ticker at", t)
}
}
}()
fmt.Println("before sleep")
//main goroutine等待5秒钟,然后ticker.Stop
time.Sleep(5 * time.Second)
ticker.Stop()
done <- true
fmt.Println("ticker stopped")
}
需要main goroutine中time.Sleep()
等待结束。
系统实现过程中,会经常用到日期、时间。
默认的time.time类型表示了精度为纳秒的时间类型。可以通过不同的方法获取数据。
(1)time.Now()
函数返回当前的本地时间
//获取当前日期
curTime := time.Now()
curTimeUTC := time.Now().UTC()
fmt.Println("the curTime is:", curTime)
//UTC时间,与北京时间相差8小时
fmt.Println("the curTimeUTC is:", curTimeUTC)
(2)返回time对象的年月日
curTime = time.Now()
fmt.Println("year is:", curTime.Year())
fmt.Println("month is:", curTime.Month())
fmt.Println("day is:", curTime.Day())
获取time对象中的年、月、日。
如果需要为特定的日期创建time对象,可以通过time.Date()方法:
specTime := time.Date(2021, time.July, 10, 18, 0, 0, 20*1000*1000, time.Local)
fmt.Println("the specific time is:", specTime)
创建特定日期:2021年7月10日,18点0分0秒,20毫秒,第7个参数为纳秒。
输出结果为:
通过特定字符串排列格式化输出time类型。
(1)RFC1123格式
curTime = time.Now()
fmt.Println(curTime)
//通过RFC1123中规定的格式输出
fmt.Println(curTime.Format(time.RFC1123))
按照RFC1123格式输出:
(2)