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來發送請求,進行更新和查詢數據