go 笔记
fmt格式化字符串
格式:%[旗标][宽度][.精度][arg索引]动词
旗标有以下几种:
+: 对于数值类型总是输出正负号;对于%q(%+q)保证只输出ASCII编码的字符
-: 在右边进行宽度填充,而不是默认的左边
空格: 对于数值类型的正数,保留一个空白的符号位
0: 用"0"进行宽度填充而不用空格,对于数值类型,符号将被移到所有0的前面
#: 备用格式:为八进制添加前缀0(%#o);为十六进制添加前缀0x(%#x)或者0X(%#X);为%p(%#p)去掉前缀0x;
(其中 "0" 和 "-" 不能同时使用,优先使用 "-" 而忽略 "0")
fmt.Printf("%010.2[2]s","abc","edf") //00000000ed
fmt.Printf("%010.4[1]s","abcd","edf") //000000abcd
变量
-
python和go中变量的不同
在go语言中当使用等号"="将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将i的值进行了拷贝,对于可变对象拷贝的是对象的地址。
而python中没有类型,类型属于对象。使用等号"="将一个变量的值赋值给另一个变量时,如:j = i,i和j指向了相同的对象,当i重新赋值时,则i指向新的对象j不会发生改变.
python:>>> a=30 >>> b=30 >>> id(a) 139846183992768 >>> id(b) 139846183992768 >>> c=a >>> id(c) 139846183992768
golang
package main import ( "fmt" ) func main() { a := 10 b := 10 c := a fmt.Printf("a address: %p\n",&a) fmt.Printf("b address: %p\n",&b) fmt.Printf("c address: %p\n",&c) } /* output: a address: 0x1040e0f8 b address: 0x1040e0fc c address: 0x1040e130 */
-
多变量使用":="赋值时必须保证至少有一个变量是新的未声明的变量
var err error fn, err := os.Open("demo.go") defer fn.Close() if err != nil { panic(err) } /*fn, err := os.Open("demo.go") // no new variables on left side of := defer fn.Close() if err != nil { panic(err) } */ fn1, err := os.Open("demo.go") defer fn1.Close() if err != nil { panic(err) }
-
go语言中任何类型的变量都会存在零值
func Test_vrrar(t *testing.T) { var i int var s string var st struct { name string } var sarr []string var marr map[string]int fmt.Printf("string:'%s'\n", s) fmt.Printf("int:'%d'\n", i) fmt.Printf("struct:'%#v'\n", st) fmt.Printf("slice:'%#v'\n", sarr) fmt.Printf("map:'%#v'\n", marr) //相同字符输出N次 fmt.Println(strings.Repeat("x",10)) if sarr != nil { panic("ok") } if marr != nil { panic("ok") } } /*output === RUN Test_vrrar string:'' int:'0' struct:'struct { name string }{name:""}' slice:'[]string(nil)' map:'map[string]int(nil)' --- PASS: Test_vrrar (0.00s) PASS ok base/variable/test 0.068s */
-
整数类型的最大值
package main import ( "fmt" ) func main() { var a uint32 a = 1 fmt.Println(a<<32-1) #1向左移动最大值即为最大值 }
goto语句
当goto语句在label之前时,在goto语句和label之间初始化变量会发生异常:
package main
import "fmt"
func main() {
goto ABC
b := 20 //goto ABC jumps over declaration of b at demo5/t.go:8
fmt.Printf("test goto b=%d\n", b)
ABC:
fmt.Println("goto over")
}
defer
使用defer改变函数的返回值
func main() {
var i = f()
fmt.Println(i) // ouput: 2
}
func f() (ret int) {
defer func(){
ret = 2
}()
return 0
}
数组和切片
go语言中基本类型都为值类型 , 且数组也为值类型, 切片是引用类型
slice长度可变是指可以通过"重新切片" 来达到容量上线,(append也可以实现扩容, 但当切片容量超过原有的容量时,切片空间扩充一倍)
var s1=make([]string,0,10)
var arr1=[9]string{"a","b","c","d","e","f","g"}
s1 = arr1[:]
fmt.Printf("len(s1)=%d,cap(s1)=%d\n",len(s1),cap(s1))
//len(s1)=9,cap(s1)=9
//打印内存地址
fmt.Printf("%p\n",s1)
//数据的赋值发生内存拷贝
var a1 [3]int = [3]int{1, 3, 4}
a2 := a1
fmt.Printf("a1:%p\n", &a1) // a1:0xc042010360
fmt.Printf("a2:%p\n", &a2) //a2:0xc042010380
切片重组:
funcslice_merge(){
s1:=make([]int,0,20)
for i:=0;i<cap(s1);i++{
s1=s1[0:len(s1)+1]//通过使用slice重组,使切片的长度达到最大容量
fmt.Printf("s1currentcaps=%d,lens=%d\n",cap(s1),len(s1))
}
}
如果s2是一个 slice,你可以将 s2 向后移动一位 s2 = s2[1:],但是末尾没有移动。切片只能向后移动,s2 = s2[-1:] 会导致编译错误。切片不能被重新分片以获取数组的前一个元素
切片为引用类型,使用make和new创建一个切片,以下两种创建方式相同:
make([]int, 10, 20)
new([20]int)[0:10]
切片:
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
下面的代码描述了从拷贝切片的 copy 函数和向切片追加新元素的 append 函数。
当在函数中更改参数切片的元素时,会更改该参数 变量的值:可以使用copy方法:
s :=[]int{2,3,4,5,6,7}
s1 := make([]int, len(s))
copy(s1,s)
//go 中函数调用的省略号:
functest6(){
vara=make([]byte,20)
copy(a[12:],"ccccc")
fmt.Println(a)
a=append(a,"ffff"...) //使用了省略号(…)来自动展开切片
fmt.Println(a)
}
切片容量的收缩与扩容
package main
import (
"log"
)
func main() {
var s = []int{1, 2, 3, 4, 5, 6, 7}
log.Printf("len(s)=%d,cap(s)=%d\n", len(s), cap(s))
log.Println("使用append扩增slice(s)容量")
s = append(s, 8)
log.Printf("len(s)=%d,cap(s)=%d\n", len(s), cap(s))
log.Printf("slice(s)=%v\n", s)
log.Printf("slice(s) ")
s = s[:5]
log.Printf("len(s)=%d,cap(s)=%d\n", len(s), cap(s))
log.Printf("slice(s)=%v\n", s)
log.Printf("slice(s)扩增")
s = s[:12]
log.Printf("len(s)=%d,cap(s)=%d\n", len(s), cap(s))
log.Printf("slice(s)=%v\n", s)
}
/*OUTPUT
2016/06/27 16:25:17 len(s)=7,cap(s)=7
2016/06/27 16:25:17 使用append扩增slice(s)容量
2016/06/27 16:25:17 len(s)=8,cap(s)=14
2016/06/27 16:25:17 slice(s)=[1 2 3 4 5 6 7 8]
2016/06/27 16:25:17 slice(s)收缩
2016/06/27 16:25:17 len(s)=5,cap(s)=14
2016/06/27 16:25:17 slice(s)=[1 2 3 4 5]
2016/06/27 16:25:17 slice(s)扩增
2016/06/27 16:25:17 len(s)=12,cap(s)=14
2016/06/27 16:25:17 slice(s)=[1 2 3 4 5 6 7 8 0 0 0 0]
*/
使用bufio的Scanner读取文本:
package main
import (
"bufio"
"log"
"strings"
)
/*
使用bufio的Scanner读取文本
*/
func main() {
input := strings.NewReader("小名 and 小明 and 小名 and 小米 and 小华 is different ")
wMap := make(map[string]int)
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanWords)
for scanner.Scan() {
wMap[scanner.Text()]++
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
log.Println(wMap)
}
MAP
map 是 引用类型 的: 内存用 make 方法来分配。
//map 的初始化
var map1[keytype]valuetype = make(map[keytype]valuetype)。
//或者简写为:map1 := make(map[keytype]valuetype)。
//判断map类型key是否存在时可以使用isPresent 形式:
var d1=map[int]string{1:"abc",2:"eeeee"}
if val,ok:=d1[1];ok{
fmt.Print(val)
}
序列化
- json序列化与反序列化
package main import ( "encoding/json" "log" ) type Person struct { //omitempty:当改字段的值为0值或未初始化时,则不现实该值 Name string `json:"name,omitempty"` Age int `json:"age,omitempty"` } func main() { var p = []Person{Person{Name: "kanghw", Age: 12}, Person{}, Person{Name: "pp"}, Person{Age: 10}} out, _ := json.MarshalIndent(p, "", " ") log.Printf("\n%s\n", string(out)) var ps []Person //反序列化 if err := json.Unmarshal(out, &ps); err != nil { log.Fatal(err) } log.Printf("%#v\n", ps) }
interface:
-
sort排序
结构体类型,自定义多列排序
package main import ( "fmt" "os" "sort" "text/tabwriter" "time" ) type Track struct { Title string Artist string Album string Year int Length time.Duration } var tracks = []*Track{ {"Go", "Delilah", "From the Roots Up", 2012, length("3m38s")}, {"Go", "Moby", "Moby", 1992, length("3m37s")}, {"Go Ahead", "Alicia Keys", "As I Am", 2007, length("4m36s")}, {"Ready 2 Go", "Martin Solveig", "Smash", 2011, length("4m24s")}, } func length(s string) time.Duration { d, err := time.ParseDuration(s) if err != nil { panic(s) } return d } func printTracks(tracks []*Track) { const format = "%v\t%v\t%v\t%v\t%v\t\n" tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0) fmt.Fprintf(tw, format, "Title", "Artist", "Album", "Year", "Length") fmt.Fprintf(tw, format, "-----", "------", "-----", "----", "------") for _, t := range tracks { fmt.Fprintf(tw, format, t.Title, t.Artist, t.Album, t.Year, t.Length) } tw.Flush() // calculate column widths and print table } type customSort struct { t []*Track less func(x, y *Track) bool } func (x customSort) Len() int { return len(x.t) } func (x customSort) Less(i, j int) bool { return x.less(x.t[i], x.t[j]) } func (x customSort) Swap(i, j int) { x.t[i], x.t[j] = x.t[j], x.t[i] } func main() { printTracks(tracks) sort.Sort(customSort{tracks, func(x, y *Track) bool { if x.Title != y.Title { return x.Title < y.Title } if x.Year != y.Year { return x.Year < y.Year } if x.Length != y.Length { return x.Length < y.Length } return false }}) fmt.Println("sorted:") printTracks(tracks) } /*output: Title Artist Album Year Length ----- ------ ----- ---- ------ Go Delilah From the Roots Up 2012 3m38s Go Moby Moby 1992 3m37s Go Ahead Alicia Keys As I Am 2007 4m36s Ready 2 Go Martin Solveig Smash 2011 4m24s sorted: Title Artist Album Year Length ----- ------ ----- ---- ------ Go Moby Moby 1992 3m37s Go Delilah From the Roots Up 2012 3m38s Go Ahead Alicia Keys As I Am 2007 4m36s Ready 2 Go Martin Solveig Smash 2011 4m24s */
-
类型断言
- x.(T):断言的动态类型x是一个具体类型T
var w io.Writer w = os.Stdout f := w.(*os.File) // success: f == os.Stdout
- x.(T):断言的动态类型x是否满足接口类型T
var w io.Writer w = os.Stdout rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
-
switch类型断言
v:= x.(type) //v的值为x转换之后的值 switch v:= x.(type) { case .... case .... }
-
reflect 反射包
如果在反射中修改值,需要在reflect.ValueOf()传递指针参数,才能达到修改源数据的目的,并使用Elem() 函数
在结构体中只有名称首字母大写的字段才是可设置的package main import ( "fmt" "reflect" ) type Person struct { Name string age int } func (this Person) GName() string { return this.Name } func (this *Person) Setage(age int) { this.age = age } func main() { p := Person{"pp", 12} v := reflect.ValueOf(&p).Elem() typeofp1 := v.Type() for i := 0; i < reflect.TypeOf(p).NumField(); i++ { fmt.Printf("Field %v:%v\n", typeofp1.Field(i).Name, v.Field(i)) //使用CanSet()判断object能否进行修改 fmt.Println(v.Field(i).CanSet()) } v.Field(0).SetString("kanghw") //panic: reflect: reflect.Value.SetInt using value obtained using unexported field //v.Field(1).SetInt(22) fmt.Println(p) for i := 0; i < reflect.ValueOf(p).NumMethod(); i++ { fmt.Printf("Method %d:%v\n", i, v.Method(i)) //函数使用:Method(n).Call(nil) fmt.Println(v.Method(i).Call(nil)) } } /*output: Field Name:pp true Field age:12 false {kanghw 12} Method 0:0x487570 [kanghw] */
-
string数组或int数组等数组,不能直接赋值给空接口数组
package main type obj interface{} func main() { s := []string{"aa", "bb", "cc"} arr_o := make([]obj, len(s)) /*arr_o = s cannot use s (type []string) as type []obj in assignment */ for index, val := range s { arr_o[index] = val } }
-
结构体,集合,高级函数例子:
package main import ( "fmt" ) type Any interface{} type Car struct { Model string Manufacturer string BuildYear int } type Cars []*Car //Process func (cs Cars) Process(f func(car *Car)) { for _, c := range cs { f(c) } } //FindAll func (cs Cars) FindAll(f func(car *Car) bool) Cars { cars := make([]*Car, 0) cs.Process(func(c *Car) { if f(c) { cars = append(cars, c) } }) return cars } //Map func (cs Cars) Map(f func(car *Car) Any) []Any { res := make([]Any, 0) cs.Process(func(c *Car) { res = append(res, f(c)) }) return res } func MakeSortedAppender(manufacturers []string) (func(car *Car), map[string]Cars) { sortedCars := make(map[string]Cars) for _, m := range manufacturers { sortedCars[m] = make([]*Car, 0) } sortedCars["Default"] = make([]*Car, 0) appender := func(c *Car) { if _, ok := sortedCars[c.Manufacturer]; ok { sortedCars[c.Manufacturer] = append(sortedCars[c.Manufacturer], c) } else { sortedCars["Default"] = append(sortedCars["Default"], c) } } return appender, sortedCars } func main() { ford := &Car{"Fiesta", "Ford", 2008} bmw := &Car{"XL 450", "BMW", 2001} merc := &Car{"D600", "Mercedes", 2009} benchu := &Car{"X750", "BEMCHI", 2011} allCars := Cars([]*Car{ford, bmw, merc, benchu}) //所有2000年之后出产的BMW汽车 allNewBMWs := allCars.FindAll(func(c *Car) bool { return (c.Manufacturer == "BMW") && (c.BuildYear > 2000) }) fmt.Println("All Cars:", allCars) fmt.Println("New BMW:", allNewBMWs) // manufacturers := []string{"Ford", "Aston Martin", "Land Rover", "BMW", "Jagur"} sortedAppender, sortedCars := MakeSortedAppender(manufacturers) allCars.Process(sortedAppender) fmt.Println("Map sortedCars: ", sortedCars) BMWCount := len(sortedCars["BMW"]) fmt.Println("We have ", BMWCount, " BMWS") }
-
当一个变量进行断言判断改变量是否实现某接口,则变量必须为接口类型,否则程序将panic
package main import ( "fmt" "strconv" ) type Per interface { String() } type Person struct { Name string Age int } func (this Person) String() { fmt.Println(this.Name + strconv.Itoa(this.Age)) } func main() { p := Person{"kang", 13} /* if p, ok := p.(Per); ok { fmt.Println(p) } */ //invalid type assertion: p.(Per) (non-interface type Person on left) var obj interface{} obj = p if obj, ok := obj.(Per); ok { fmt.Println(obj) } }
-
当切片类型做为接受者时,切片方法中需要改变切片的长度时,该参数必须为指针类型, 否则会无法成功修改该接受者
package main import ( "fmt" ) type obj interface{} type stacker interface { push(obj) pop() obj Len() int } type stack []obj func (this stack) Len() int { return len(this) } func (this *stack) pop() obj { //this 必须为指针类型 stk := *this if stk.Len() == 0 { panic("stack is empty") } res := stk[stk.Len()-1] *this = stk[0 : stk.Len()-1] return res } func (this *stack) push(em obj) { stk := *this *this = append(stk, em) } func main() { test := stack([]obj{"a", "b", "d"}) fmt.Println(test.pop()) test.push("sdfsdf") fmt.Println(test) }
-
结构体零值,空指针的区别
type Person struct { name string age int } func main() { var p1 *Person //(*main.Person)(nil) var p2 Person //main.Person{name:"", age:0} var p3 = new(Person) //*main.Person{name:"", age:0} //p1为空指针
读取用户输入
-
使用fmt包的Scan和Sscan读取输入
var firstName, lastName, s string var f float32 var i int input := "12.1:20:aaa" format := "%f:%d:%s" fmt.Println("please enter your name:") fmt.Scanln(&firstName, &lastName) //必须使用&符号 fmt.Sscanf(input, format, &f, &i, &s)
-
使用bufio包提供的缓冲读取
endstr := '\n' //控制结束字符 var reader *bufio.Reader var format = "%s %s %d" var name, sex string var age int reader = bufio.NewReader(os.Stdin) fmt.Printf("please your name sex age, format is %s:\n", format) output, err := reader.ReadString(endstr)
读取文件
-
使用bufio包读取
fn, err := os.Open(filename) reader := bufio.NewReader(fn) //line, _, err := reader.ReadLine() line, err := reder.ReadString('\n')
-
使用切片读取
buf := make([]byte, 1024) n, err := fn.Read(buf)
-
使用io/ioutil包读取
buf, err := ioutil.ReadFile(filename)
-
写入文件
fn ,_ := os.OpenFile(filename, os.O_APPEND|os.O_CREATE,0644) var wirter = bufio.NewWriter(fn) n, _ := writer.WriteString(str) writer.Flush() // 不进行刷新buffer将不能写入文件
json
-
使用"encoding/json"对struct进行序列化时字段名为小写将不能被导出
package main import ( "encoding/json" "fmt" ) type Address struct { Country string Area string } type Person struct { Name string age int //age字段不能被导出 Addresses []*Address } func main() { add1 := &Address{"中国", "河南"} add2 := &Address{"中国", "北京"} add3 := &Address{"澳洲", "悉尼"} p := Person{"pp", 23, []*Address{add1, add2, add3}} //fmt.Printf("%#v\n", p) var vals []byte var err error //vals, err = json.Marshal(p)//无格式化的输出 vals, err = json.MarshalIndent(p, "", " ") if err != nil { panic(err) } else { fmt.Printf("%s\n", vals) } } /*output: { "Name": "康海伟", "Addresses": [ { "Country": "中国", "Area": "河南" }, { "Country": "中国", "Area": "北京" }, { "Country": "澳洲", "Area": "悉尼" } ] } */
-
json写入文件,及反序列化
fn, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0666) wrio := json.NewEncoder(fn) wrio.Encode(any) //写入文件 erio := json.NewDecoder(fn) erio.Decode(&p) //将反序列化之后的数据存入变量p中 json.Unmarshal([]byte(var),&p) //将反序列化之后的数据存入变量p中
channel
-
golang中通道默认是阻塞的,如果通道处于等待状态,程序无法处理时将发生deadlock
对于同一个通道,发送操作(协程或者函数中的),在接收者准备好之前是阻塞的:如果ch中的数据无人接收,就无法再给通道传入其他数据:新的输入无法在通道非空的情况下传入。所以发送操作会等待 ch 再次变为可用状态:就是通道值被接收时(可以传入变量)。
对于同一个通道,接收操作是阻塞的(协程或函数中的),直到发送者可用:如果通道中没有数据,接收者就阻塞了。
//死锁 package main import ( "fmt" "time" ) func main() { ch := make(chan bool) //ch <- false //发生死锁,程序运行该处时处于等待状态,不能继续运行 :fatal error: all goroutines are asleep - deadlock! go func() { fmt.Println(<-ch) }() ch <- false //放置该位置死锁将不会发生,因为已启动一个协程,等待ch通道放入数据 time.Second(1e9) //1e9 == 1 * time.Second }
-
使用for循环通道需要显示close channel不然会发生死锁
package main import ( "fmt" ) func main() { ch := make(chan string) go func() { ch <- "sdfsfdsf" ch <- "sdfsfdsf" close(ch) //如果没有close:fatal error: all goroutines are asleep - deadlock! }() for c := range ch { fmt.Println(c) } }
-
生产者消费者模式
/* 生产者消费者模式 */ func product(n int) chan int { ch := make(chan int) go func() { for i := 0; i < n; i++ { ch <- i } }() return ch } func consumer(n int, ch chan int) { go func() { for i := 0; i < n; i++ { fmt.Println(<-ch) } }() } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) var n = 10000 consumer(n, product(n)) time.Sleep(5 * time.Second) }
测试test
对包进行单元测试时,测试程序必须属于被测试的包,并且文件名为*_test.go,该文件只在运行go test时进行编译,测试函数名d的规范为TestFuncDesc, 已Test开头,后面为测试的函数表述
xorm序列化module
type resUseroverview struct {
Cdate string `json:"d"`
SumAddUser int64 `json:"SumAU"`
SumAddUserPhone int64 `json:"SumAUP"`
SumAddUserDeviceid int64 `json:"SumAUD"`
SumUserAct int64 `json:"SumUA"`
SumMvalueCount int64 `json:"SumMC"`
SumMvalueMva int64 `json:"SumMM"`
SumActCount int64 `json:"SumAC"`
SumActMva int64 `json:"SumAM"`
}
//json为:序列化json中key的值
//而字段为名"SumAddUser" 与sql语句查询中的字段的别名相关,如“SumAddUser”语句中的别名必须为sum_add_user, 结构体中的字段名的在"sql语句中"大写字母前需要加下划线(首字母除外),全部转换为小写
os模块
使用os模块创建文件,打开标记:
O_RDONLY:只读模式(read-only)
O_WRONLY:只写模式(write-only)
O_RDWR:读写模式(read-write)
O_APPEND:追加模式(append)
O_CREATE:文件不存在就创建(create a new file if none exists.)
O_EXCL:与 O_CREATE 一起用,构成一个新建文件的功能,它要求文件必须不存在(used with O_CREATE, file must not exist)
O_SYNC:同步方式打开,即不使用缓存,直接写入硬盘
O_TRUNC:打开并清空文件
数据竞争
只要不同的gorouting访问同一个变量,且一个存在更改操作,就存在数据竞争, 避免数据的方法:
- 避免多个gorouting访问数据,使数据的读写都在一个gorouting,其他gorouting不能直接访问变量,必须使用channel来发送请求,进行更新和查询数据