###### tags: `GoLang` # GoLang 04.應用範例: ### Go : 語言的包(Package)使用說明 > > 1. 每個程式碼 ``.go``必須歸屬一個package > 2. 歸屬同一個package之 ``.go``變數名稱不得重複 > 3. 歸屬同一個package之 ``.go``檔案必須位於同一資料夾 > 4. import導入package可自訂抬頭名稱,資料夾路徑為 ``%GOPATH%/src``目錄下 > 5. 若要將package編譯成執行檔,必須將主程式歸屬於 ``package main``當中,執行檔將由 ``func init()``然後 ``func main()``開始執行。 > 6. ``func init()``由import內引用package的倒序開始執行,再依自身包``func init()``順序開始執行(同一包可准許內有多個``func init()``)。 > > ### Package使用範例: > > ```graphviz > > digraph{ > > nodesep=0.5 > ranksep=0.5 > graph [bgcolor="#1e1e1e"] > edge [ color="#DDDDDD"] > node [ color="#3e4045" penwidth=10 fillcolor="#3e4045" fontcolor="#DDDDDD" style=filled shape=ellipse fixedsize=true width=1.9] > > "\%GOPATH\%\" -> "src\" -> "Lesson2\" -> "Package演示\" -> {"Package_FolderTest1\" "Package_FolderTest2\" "Package_FolderTest3\"} > > "Package演示\" -> "Package_main.go" > [fillcolor=Blue] > > "Package_FolderTest1\" -> "Package_Test1.go" > [fillcolor=Blue] > > "Package_FolderTest2\" -> {"Package_Test2-1.go" "Package_Test2-2.go"} > [fillcolor=Blue] > > "Package_FolderTest3\" -> {"Package_Test3-1.go" "Package_Test3-2.go"} > [fillcolor=Blue] > } > > ``` > > > ``Package_main.go:`` > ``` go= > package main > import ( > Package_CustomTest1 "Lesson2/Package演示/Package_FolderTest1" > Package_CustomTest2 "Lesson2/Package演示/Package_FolderTest2" > Package_CustomTest3 "Lesson2/Package演示/Package_FolderTest3" > "fmt" > ) > > func main() { > > fmt.Println(Package_CustomTest1.Gstring_Name_Test1) > fmt.Println(Package_CustomTest2.Gstring_Name_Test2_1) > fmt.Println(Package_CustomTest2.Gstring_Name_Test2_2) > fmt.Println(Package_CustomTest3.Gstring_Name_Test2_1) > fmt.Println(Package_CustomTest3.Gstring_Name_Test2_2) > > } > ``` > > ``Package_Test1.go:`` > ``` go= > //package main可透過import導入並使用大寫開頭名稱之變數 > //package main不可由import導入小寫開頭名稱之變數 > package Package_Test1 > var gstring_Name_Test0 = "不可import" > var Gstring_Name_Test1 = "測試1" > ``` > > ``Package_Test2-1.go:`` > ``` go= > //歸屬同一個package(同目錄)的Package_Test2-1.go之變數名稱不得重複 > package Package_Test2 > var Gstring_Name_Test2_1 = "測試2-1" > ``` > > ``Package_Test2-2.go:`` > ``` go= > //歸屬同一個package(同目錄)的Package_Test2-2.go之變數名稱不得重複複 > package Package_Test2 > var Gstring_Name_Test2_2 = "測試2-2" > ``` > > ``Package_Test3-1.go:`` > ``` go= > //package main可使用import更名避免重複的package名稱 > package Package_Test2 > var Gstring_Name_Test2_1 = "測試2-1" > ``` > > ``Package_Test2-2.go:`` > ``` go= > //package main可使用import更名避免重複的package名稱 > package Package_Test2 > var Gstring_Name_Test2_2 = "測試2-2" > ``` ### Go : 驗證CG記憶體回收機制 > ```go= > package main > > import ( > "fmt" > "log" > "runtime"package main > > import ( > "fmt" > "log" > "runtime" > ) > > func main() { > > var m0 runtime.MemStats > println() > log.Println("At begin:") > PrintMemUsage(m0) > // 2020/04/06 11:09:40 At begin: > // Alloc(當前堆上對象佔用的內存大小) = 0 MiB > // Sys(程序從作業系統總共申請的內存大小) = 6 MiB > // NumGC(垃圾回收運行的次數) = 0 > > var m1 runtime.MemStats > var overall1 []float64 > for i := 0; i < 1136640; i++ { > overall1 = append(overall1, 1) > } > println() > log.Println("nothing to do:") > PrintMemUsage(m1) > println(overall1) > overall1 = nil //僅為了計算方便,不影響測試結果 > // 2020/04/06 11:09:40 nothing to do: > // Alloc(當前堆上對象佔用的內存大小) = 8 MiB > // Sys(程序從作業系統總共申請的內存大小) = 37 MiB > // NumGC(垃圾回收運行的次數) = 9 > // [1136640/1136640]0xc00127c000 > > var m2 runtime.MemStats > var overall2 []float64 > for i := 0; i < 1136640; i++ { > overall2 = append(overall2, 1) > } > overall2 = nil > println() > log.Println("nil :") > PrintMemUsage(m2) > println(overall2) > // 2020/04/06 11:09:40 nil : > // Alloc(當前堆上對象佔用的內存大小) = 0 MiB > // Sys(程序從作業系統總共申請的內存大小) = 37 MiB > // NumGC(垃圾回收運行的次數) = 17 > // [0/0]0x0 > > // Force GC to clear up, should see a memory drop > var m3 runtime.MemStats > var overall3 []float64 > for i := 0; i < 1136640; i++ { > overall3 = append(overall3, 1) > } > runtime.GC() > println() > log.Println("Forece GC:") > PrintMemUsage(m3) > // 2020/04/06 11:09:40 Forece GC: > // Alloc(當前堆上對象佔用的內存大小) = 0 MiB > // Sys(程序從作業系統總共申請的內存大小) = 37 MiB > // NumGC(垃圾回收運行的次數) = 27 > > > var m4 runtime.MemStats > var overall4 []float64 > p4 := &overall4 > for i := 0; i < 1136640; i++ { > overall4 = append(overall4, 1) > } > runtime.GC() > println() > log.Println("Forece GC (use pointer):") > PrintMemUsage(m4) > println(p4) > // 2020/04/06 11:09:40 Forece GC: > // Alloc(當前堆上對象佔用的內存大小) = 8 MiB > // Sys(程序從作業系統總共申請的內存大小) = 37 MiB > // NumGC(垃圾回收運行的次數) = 37 > // 0xc00010ff60 > > > } > > func PrintMemUsage(m runtime.MemStats) { > runtime.ReadMemStats(&m) > > // For info on each, see: https://golang.org/pkg/runtime/#MemStats > fmt.Printf("Alloc(當前堆上對象佔用的內存大小) = %v MiB\n", byteToMiB(m.Alloc)) > //fmt.Printf("TotalAlloc(堆上總共分配出的內存大小) = %v MiB\n", byteToMiB(m.TotalAlloc)) > fmt.Printf("Sys(程序從作業系統總共申請的內存大小) = %v MiB\n", byteToMiB(m.Sys)) > fmt.Printf("NumGC(垃圾回收運行的次數) = %v\n", m.NumGC) > > } > > func byteToMiB(b uint64) uint64 { > return b / 1024 / 1024 > } > ``` ### Go : 氣泡排序法(Bubble Sort): > 氣泡排序演算法的運作如下: > 1.比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。 > 2.對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。 > 這步做完後,最後的元素會是最大的數。 > 3.針對所有的元素重複以上的步驟,除了最後一個。 > 4.持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。 ><iframe width=100% height="315" src="https://www.youtube.com/embed/O2w-gJjU-PU?start=266" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> > > ```go > package main > import "fmt" > func main() { > > var vSlice = []int{51, 99, 36, 66, 62} > fmt.Println("排序前:", vSlice) > fmt.Println("最後結果:", fcBubbleSort_Slice(vSlice)) > > } > > func fcBubbleSort_Slice(inputSlice []int) []int { > for index1 := 1; index1 < len(inputSlice); index1++ { > // 第一層迴圈負責控制排序輪迴數 > // 每輪都從第二個元素開始,將最大的值交換到最後一位 > for index2 := 0; index2 < len(inputSlice)-index1; index2++ { > // 第二層迴圈負責比較兩元素大小判斷是否交換 > if inputSlice[index2] > inputSlice[index2+1] { > inputSlice[index2], inputSlice[index2+1] = inputSlice[index2+1], inputSlice[index2] > } > } > fmt.Printf("第%v次排序結果: %v\n", index1, inputSlice) > } > return inputSlice > } > ``` ### Go : 反射包(reflect)介紹 > ```go= > package main > > import ( > "fmt" > "reflect" > ) > > func fc_ReflectExample1(if_input interface{}) { > fmt.Printf("%v (%T)\n", reflect.ValueOf(if_input), reflect.ValueOf(if_input)) > var vi_int = 1 + reflect.ValueOf(if_input).Int() //使用反射之內建方法進行型別斷言 > fmt.Printf("%v (%T)\n", vi_int, vi_int) > } > > type ty_Custom struct { > Name string > ID uint > } > > func fc_ReflectExample2(if_input interface{}) { > if st_Custom, TypePass := if_input.(ty_Custom); TypePass { > fmt.Printf("%v (%T)\n", st_Custom, st_Custom) > fmt.Printf("%v (%T)\n", st_Custom.Name, st_Custom.Name) > } > > } > > func main() { > > fc_ReflectExample1(9) > println() > fc_ReflectExample2(ty_Custom{Name: "測試"}) > > // 9 (reflect.Value) > // 10 (int64) > > // {測試 0} (main.ty_Custom) > // 測試 (string > > } > ``` > > #### reflect包常用函式與方法: > | 函式名稱 | 說明| 輸入 | 輸出類型 | > | - | - | - | - | > |reflect.ValueOf(任意變數)|回傳變數值|任意變數|(reflect.Value)| > |reflect.TypeOf(任意變數)|回傳變數型別名稱|任意變數|(*reflect.rtype)| > |(reflect.Value型別變數).Kind()|回傳變數底層型別|reflect.Value型別|(string)| > |(reflect.Value型別變數).Elem()|回傳指標之變數值|具備指標型別|(reflect.Value))| > |(reflect.Value型別變數).Elem().FieldByName(屬性名稱)|回傳結構體該屬性值|(reflect.Value型別)和屬性名稱|(reflect.Value)| > |(reflect.Value型別變數名稱).CanSet()|判斷該變數是否能修改值|reflect.Value型別|(bool)| > |(reflect.Value型別變數名稱).Set(reflect.Value(數值))|直接修改該變數值|符合型別之數值|| > |(*reflect.rtype).Name()|回傳變數型別名稱|(*reflect.rtype)|型別名稱(string)| > > ``` > #### reflect Kind() 反射type型別名稱常數 > const ( > Invalid Kind = iota //nil屬此類 > Bool > Int > Int8 > Int16 > Int32 > Int64 > Uint > Uint8 > Uint16 > Uint32 > Uint64 > Uintptr > Float32 > Float64 > Complex64 > Complex128 > Array > Chan > Func > Interface > Map > Ptr > Slice > String > Struct > UnsafePointer > ) > ``` > > ```go= > package main > > import ( > "fmt" > "reflect" > ) > > func fc_ReflectExample1(if_input interface{}) { > fmt.Printf("%v (%T)\n", reflect.ValueOf(if_input), reflect.ValueOf(if_input)) > var vi_int = 1 + reflect.ValueOf(if_input).Int() //使用反射之內建方法進行型別斷言 > fmt.Printf("%v (%T)\n", vi_int, vi_int) > } > > type ty_Custom struct { > Name string > ID uint > } > > func fc_ReflectExample2(if_input interface{}) { > if st_Custom, TypePass := if_input.(ty_Custom); TypePass { > fmt.Printf("%v (%T)\n", st_Custom, st_Custom) > fmt.Printf("%v (%T)\n", st_Custom.Name, st_Custom.Name) > } > > } > > func main() { > > fc_ReflectExample1(9) > println() > fc_ReflectExample2(ty_Custom{Name: "測試"}) > > // 9 (reflect.Value) > // 10 (int64) > > // {測試 0} (main.ty_Custom) > // 測試 (string > > } > ``` > ### Go 反射包(reflect)應用: > > ```go= > package main > import ( > "fmt" > "reflect" > ) > > type ty_Example struct { > F0 uint > F1 string > } > > func main() { > > fmt.Printf("%v\n", reflect.ValueOf("Testing")) //Testing > fmt.Printf("%T\n", reflect.ValueOf("Testing")) //reflect.Value > fmt.Printf("%v\n", reflect.ValueOf("Testing").Kind()) //string > fmt.Printf("%T\n", reflect.ValueOf("Testing").Kind()) //reflect.Kind > //fmt.Printf("%v\n", reflect.ValueOf("Testing").Elem()) //panic: reflect: call of reflect.Value.Elem on string Value > println() > > var vi_int int = 1 > //var rVO = reflect.ValueOf(&vi_int) > var vipr_int *int = &vi_int > var rVO = reflect.ValueOf(vipr_int) > > fmt.Printf("%v\n", rVO.Elem()) //1 > fmt.Printf("%T\n", rVO.Elem()) //reflect.Value > fmt.Printf("%v\n", rVO.Elem().CanSet()) //true > fmt.Printf("%T\n", rVO.Elem().CanSet()) //bool > rVO.Elem().Set(reflect.ValueOf(99)) > println(vi_int) //99 > println() > > var st_Example = ty_Example{F0: 1, F1: "測試"} > fmt.Printf("%v\n", reflect.ValueOf(&st_Example).CanSet()) //false > fmt.Printf("%T\n", reflect.ValueOf(&st_Example).CanSet()) //bool > fmt.Printf("%v\n", reflect.ValueOf(&st_Example).Elem().FieldByName("F1")) //測試 > fmt.Printf("%T\n", reflect.ValueOf(&st_Example).Elem().FieldByName("F1")) //reflect.Value > fmt.Printf("%v\n", reflect.ValueOf(&st_Example).Elem().FieldByName("F1").CanSet()) //true > fmt.Printf("%T\n", reflect.ValueOf(&st_Example).Elem().FieldByName("F1").CanSet()) //bool > reflect.ValueOf(&st_Example).Elem().FieldByName("F1").Set(reflect.ValueOf("修改")) > fmt.Println(st_Example) //{1 修改} > println() > > } > ``` > > ```go= > package main > import ( > "fmt" > "reflect" > ) > > type ty_Json struct { > Name string `json:"jsname"` > Age int `json:"jsage"` > } > > func main() { > > st_User := ty_Json{ > "測試", > 20, > } > > fmt.Println(reflect.TypeOf(st_User)) //(0x4adaa0,0x487ba0) > fmt.Println(reflect.TypeOf(st_User).Field(0).Name) //name > fmt.Println(reflect.TypeOf(st_User).Field(0).Type) //string > fmt.Println(reflect.TypeOf(st_User).Field(0).Tag) //json:"jsname" > > fmt.Println(reflect.ValueOf(st_User).FieldByName("Name")) //測試 > fmt.Println(reflect.ValueOf(st_User).FieldByName("Name").Type()) //測試 > > println() > > for index := 0; index < reflect.ValueOf(st_User).NumField(); index++ { > if reflect.ValueOf(st_User).Field(index).CanInterface() { //判斷是否可導出屬性 > fmt.Printf("%v (%v %v %v)\n", > reflect.ValueOf(st_User).Field(index).Interface(), > reflect.TypeOf(st_User).Field(index).Name, > reflect.TypeOf(st_User).Field(index).Type, > reflect.TypeOf(st_User).Field(index).Tag) > } > } > // 測試 (Name string json:"jsname") > // 20 (Age int json:"jsage") > > } > ``` ### Go : 語言時間與日期包(time) > 官方文檔: **Golang** → **package time** → **type Duration** > 內建計時常數如下(**const**): > > Nanosecond Duration = 1 (最小計時單位) > Microsecond = 1000 * Nanosecond > Millisecond = 1000 * Microsecond > Second = 1000 * Millisecond > Minute = 60 * Second > Hour = 60 * Minute > > **編譯時使用計時常數不可作除法運算**(設計目的是為了避免影響精確度) > > > > > ``` go= > package main > import ( > "fmt" > "time" > ) > > func main() { > > vNow := time.Now() > fmt.Printf("vNow = %v (%T)\n", vNow, vNow) > > fmt.Printf("年 = %v (%T)\n", vNow.Year(), vNow.Year()) > fmt.Printf("月 = %v (%T)\n", vNow.Month(), vNow.Month()) > fmt.Printf("月 = %v (%T)\n", int(vNow.Month()), int(vNow.Month())) > fmt.Printf("日 = %v (%T)\n", vNow.Day(), vNow.Day()) > fmt.Printf("星期 = %v (%T)\n", vNow.Weekday(), vNow.Weekday()) > fmt.Printf("小時 = %v (%T)\n", vNow.Hour(), vNow.Hour()) > fmt.Printf("分鐘 = %v (%T)\n", vNow.Minute(), vNow.Minute()) > fmt.Printf("秒 = %v (%T)\n", vNow.Second(), vNow.Second()) > > fmt.Printf("當前時間 %04d-%02d-%02d(星期%02d) %02d:%02d:%02d\n", > time.Now().Year(), int(time.Now().Month()), time.Now().Day(), time.Now().Weekday(), > vNow.Hour(), vNow.Minute(), vNow.Second()) > > fmt.Printf(vNow.Format("2006/01/02 15:04:05") + "\n") > > fmt.Printf(vNow.Format("2006/01/02") + "\n") > > fmt.Printf(vNow.Format("15:04:05") + "\n") > > fmt.Printf("Unix時間戳:%v UnixNano時間戳%v\n", vNow.Unix(), vNow.UnixNano()) > > vCounterA := fcStartTimeCounter(0) > //計數5秒鐘 > for i := 0; i < 5; i++ { > > fmt.Println(i, "秒") > time.Sleep(time.Second) > } > > fmt.Printf("總共經過了: %0.3f秒\n", float64(fcStopTimeCounter(vCounterA))/(1000*1000*1000)) > > > vCounterB := time.Now() > for i := 0; i < 5; i++ { > > fmt.Println(i, "秒") > time.Sleep(time.Second) > } > fmt.Printf("總共經過了: %0.3f秒\n",time.Now().Sub(vCounterB).Seconds()) > } > > // const ( > // Nanosecond Duration = 1 > // Microsecond = 1000 * Nanosecond > // Millisecond = 1000 * Microsecond > // Second = 1000 * Millisecond > // Minute = 60 * Second > // Hour = 60 * Minute > // ) > > func fcStartTimeCounter(input int64) int64 { > fmt.Printf("\n計時開始由 %0.3f 秒開始計時", float64(input)/(1000^3)) > return time.Now().UnixNano() + input > } > > func fcStopTimeCounter(input int64) int64 { > return time.Now().UnixNano() - input > } > > ``` > > ### Go 官方json包(json): > ```go > package main > > import ( > "encoding/json" > "fmt" > ) > > type User struct { > ID int > Name string > Money float64 > Skills []string > Relationship map[string]string > Identification Identification > } > > type Identification struct { > Phone bool > Email bool > } > > func main() { > user := User{ > ID: 1, > Name: "Tony", > Skills: []string{"program", "rich", "play"}, > Relationship: map[string]string{ > "Dad": "Hulk", > "Mon": "Natasha", > }, > Identification: Identification{ > Phone: true, > Email: false, > }, > } > b, err := json.Marshal(user) > if err != nil { > fmt.Println("error:", err) > } > fmt.Println(string(b)) > //{"ID":1,"Name":"Tony","Money":0,"Skills":["program","rich","play"],"Relationship":{"Dad":"Hulk","Mon":"Natasha"},"Identification":{"Phone":true,"Email":false}} > } > > > package main > > import ( > "encoding/json" > "fmt" > ) > > type User struct { > ID int > Name string > Money float64 > Skills []string > Relationship map[string]string > Identification Identification > } > > type Identification struct { > Phone bool `json:"phone"` > Email bool `json:"email,E-Mail"` > } > > func main() { > var jsonBlob = []byte(`{"ID":1,"Name":"Tony","Money":0,"Skills":["program","rich","play"],"Relationship":{"Dad":"Hulk","Mon":"Natasha"},"Identification":{"phone":true,"E-Mail":false}}`) > > var user User > err := json.Unmarshal(jsonBlob, &user) > if err != nil { > fmt.Println("error:", err) > } > fmt.Printf("%+v", user) > //{ID:1 Name:Tony Money:0 Skills:[program rich play] Relationship:map[Dad:Hulk Mon:Natasha] Identification:{Phone:true Email:false}} > } > > > package main > > import ( > "encoding/json" > "fmt" > ) > > type User struct { > ID int > Name string > Money float64 `json:",string"` > Skills []string > Relationship map[string]string > Identification Identification > Career string > Responsibility interface {} > } > > type Identification struct { > Phone bool `json:"phone"` > Email bool `json:"email"` > } > > func main() { > var jsonBlob = []byte(`[ > { > "ID":1, > "Name":"Tony", > "Career":"Engineer", > "Responsibility":{ > "skill":"PHP&Golang&Network", > "description":"coding" > } > }, > { > "ID":2, > "Name":"Jim", > "Career":"Manager", > "Responsibility":{ > "experienced":true > } > } > ]`) > > var users []User > if err := json.Unmarshal(jsonBlob, &users); err != nil { > fmt.Println("error:", err) > } > fmt.Printf("%#v\n", users) //[]main.User{main.User{ID:1, Name:"Tony", Money:0, Skills:[]string(nil), Relationship:map[string]string(nil), Identification:main.Identification{Phone:false, Email:false}, Career:"Engineer", Responsibility:map[string]interface {}{"description":"coding", "skill":"PHP&Golang&Network"}}, main.User{ID:2, Name:"Jim", Money:0, Skills:[]string(nil), Relationship:map[string]string(nil), Identification:main.Identification{Phone:false, Email:false}, Career:"Manager", Responsibility:map[string]interface {}{"experienced":true}}} > > fmt.Println(users[0].Responsibility.(map[string]interface{})["description"].(string)) //coding > fmt.Println(users[1].Responsibility.(map[string]interface{})["experienced"].(bool)) //true > } > > package main > > import ( > "encoding/json" > "fmt" > ) > > type User struct { > ID int > Name string > Money float64 `json:",string"` > Skills []string > Relationship map[string]string > Identification Identification > Career string > Responsibility json.RawMessage > } > > type Identification struct { > Phone bool `json:"phone"` > Email bool `json:"email"` > } > > type Engineer struct { > Skill string `json:"skill"` > Description string `json:"description"` > } > > type Manager struct { > Experienced bool `json:"experienced"` > } > > func main() { > var jsonBlob = []byte(`[ > { > "ID":1, > "Name":"Tony", > "Career":"Engineer", > "Responsibility":{ > "skill":"PHP&Golang&Network", > "description":"coding" > } > }, > { > "ID":2, > "Name":"Jim", > "Career":"Manager", > "Responsibility":{ > "experienced":true > } > } > ]`) > > var users []User > if err := json.Unmarshal(jsonBlob, &users); err != nil { > fmt.Println("error:", err) > } > > for _, user := range users { > switch user.Career { > case "Engineer": > var responsibility Engineer > if err := json.Unmarshal(user.Responsibility, &responsibility); err != nil { > fmt.Println("error:", err) > } > fmt.Println(responsibility.Description) //coding > case "Manager": > var responsibility Manager > if err := json.Unmarshal(user.Responsibility, &responsibility); err != nil { > fmt.Println("error:", err) > } > fmt.Println(responsibility.Experienced) //true > default: > fmt.Println("warning:", "don't exist") > } > } > } > ``` ### Go : 檔案管理包(os.File): > #### os.file包使用說明: > | 函式名稱 | 說明 | 輸入 | 輸出類型:::: | > | - | - | - | - | > | os.Open(路徑) |打開路徑中的檔案,並將輸出該文件的指標|(string)|(*file error)| > | (*file).Close() |將關閉file指標所指向檔案,移除移除該file指標|(string)|(error)| > > ```go > package FileSystem > import ( > "bufio" > "errors" > "io" > "io/ioutil" > "net/http" > "os" > "strconv" > ) > > // os.O_WRONLY: 只寫 > // os.O_CREATE: 創建文件(如果文件不存在) > // os.O_RDONLY: 只讀 > // os.O_RDWR: 讀寫皆可 > // os.O_APPEND: 尾部加入 > // os.O_TRUNC: 清空(文件存在情況下) > // 第三個參數設置權限控制 > > > > func fc_GetPWD() string { > vs_WorkingDir, _ := os.Getwd() > return vs_WorkingDir > } > > func fc_SetPWD(FilePathInput string) error { > err := os.Chdir(FilePathInput) > return err > } > > func fc_GetEnv(EnvVarName string) (EnvVarValue string) { > return os.Getenv(EnvVarName) > } > > func fc_GetAllEnv(EnvVarName string) (AllEnvVarValues []string) { > return os.Environ() > } > > func fc_GetPathList(FilePathInput string) (FolderList []string, err error) { > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return FolderList, err > } > > ty_FileInfos, err := pty_OpenFile.Readdir(-1) > pty_OpenFile.Close() > if err != nil { > return FolderList, err > } > for _, value := range ty_FileInfos { > FolderList = append(FolderList, value.Name()) > } > return FolderList, nil > } > > func fc_GetFolderList(FilePathInput string) (FolderList []string, err error) { > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return FolderList, err > } > > ty_FileInfos, err := pty_OpenFile.Readdir(-1) > pty_OpenFile.Close() > if err != nil { > return FolderList, err > } > for _, value := range ty_FileInfos { > if value.IsDir(){ > FolderList = append(FolderList, value.Name()) > } > } > return FolderList, nil > } > > func fc_GetFileList(FilePathInput string) (FolderList []string, err error) { > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return FolderList, err > } > > ty_FileInfos, err := pty_OpenFile.Readdir(-1) > pty_OpenFile.Close() > if err != nil { > return FolderList, err > } > for _, value := range ty_FileInfos { > if !value.IsDir(){ > FolderList = append(FolderList, value.Name()) > } > } > return FolderList, nil > } > > func fc_FileMove(FilePathInput, FilePathOutput string) error { > return os.Rename(FilePathInput, FilePathOutput) > } > > func fc_SetEnv(EnvVarName, EnvVarValue string) error { > return os.Setenv(EnvVarName, EnvVarValue) > } > > func fc_IsFileModeCode(vs_chmod string) bool { > if len(vs_chmod) == 3 || len(vs_chmod) == 4 { > _, err_chmodCheck := strconv.ParseUint(vs_chmod, 10, 32) > if err_chmodCheck == nil { > for _, value := range vs_chmod { > if value == '8' || value == '9' { > return false > } > } > return true > } > } > return false > } > > func fc_FileModeEnCode(vs_chmod string) (vs_ModeCodestring string, err error) { > switch len(vs_chmod) { > case 1: //r、w表示同時適用OWNER(Windows,Unix) > switch vs_chmod { > case "r": > return "0200", nil > case "w": > return "0400", nil > } > case 2: //rw兩字表示同時適用OWNER(Windows,Unix) > if vs_chmod == "rx" || vs_chmod == "xr" { > return "0200", nil > } else if vs_chmod == "wx" || vs_chmod == "xw" { > return "0400", nil > } else if vs_chmod == "rw" || vs_chmod == "wr" { > return "0600", nil > } > case 3: //rwx三字表示同時適用OWNER(Windows,Unix),777三數字僅適用Unix > switch vs_chmod { > case "rwx", "rxw", "xrw", "xwr", "wrx", "wxr": > return "0700", nil > default: > if fc_IsFileModeCode(vs_chmod) { > return "0" + vs_chmod, nil > } > } > case 4: > if fc_IsFileModeCode(vs_chmod) { > return vs_chmod, nil > } > case 9: > var vu_ower, vu_group, vu_others, vu_sum uint64 > for index, value := range vs_chmod { > switch value { > case 'r': > vu_sum = vu_sum + 4 > case 'w': > vu_sum = vu_sum + 2 > case 'x': > vu_sum = vu_sum + 1 > case '-': > continue > default: > return "", errors.New("Bad chmod input") > } > > switch index { > case 2: > vu_ower = vu_sum > vu_sum = 0 > case 5: > vu_group = vu_sum > vu_sum = 0 > case 8: > vu_others = vu_sum > case 9: > return "", errors.New("Bad chmod input") > } > } > > if vu_ower > 7 || vu_group > 7 || vu_others > 7 { > return "", errors.New("Bad chmod input") > } else { > return "0" + strconv.FormatUint(vu_ower, 10) + strconv.FormatUint(vu_group, 10) + strconv.FormatUint(vu_others, 10), nil > } > > } > return "", errors.New("Bad chmod input") > } > > func fc_IsExist(FilePathInput string) bool { > if _, err := os.Stat(FilePathInput); os.IsExist(err) { > return false > } > return true > } > > func fc_IsNotExist(FilePathInput string) bool { > if _, err := os.Stat(FilePathInput); os.IsNotExist(err) { > return true > } > return false > } > > func fc_CreatFile(FilePathInput string) error { > _, err := os.Create(FilePathInput) //666 > return err > } > > func fc_CreatFolder(FilePathInput, vs_chmod string) error { > var err error > if vs_chmod == "" { > err = os.MkdirAll(FilePathInput, 0777) > return err > } > > vu_chmodNum, err_chmodCheck := strconv.ParseUint(vs_chmod, 10, 32) > if err_chmodCheck != nil { > err = os.MkdirAll(FilePathInput, 0777) > } else { > err = os.MkdirAll(FilePathInput, os.FileMode(vu_chmodNum)) > } > > return err > } > > func fc_RemovePath(FilePathInput string) error { > err := os.RemoveAll(FilePathInput) > return err > } > > func fc_RemoveFile(FilePathInput string) error { > ty_FileStat, err := os.Stat(FilePathInput) > if !ty_FileStat.IsDir() { > err = os.Remove(FilePathInput) > return err > } else { > return errors.New("It's a Folder not File") > } > } > > func fc_RemoveFolder(FilePathInput string) error { > ty_FileStat, err := os.Stat(FilePathInput) > if ty_FileStat.IsDir() { > err = os.Remove(FilePathInput) > return err > } else { > return errors.New("It's a File not Empty-Folder") > } > } > > func fc_ReadFile(FilePathInput string) (string, error) { > > sl_Output, err := ioutil.ReadFile(FilePathInput) > if err != nil { > return "", err > } else { > return string(sl_Output), nil > } > > } > > func fc_ReadBufFile(FilePathInput string, BufSize int64) (string, error) { > > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return "", err > } > defer pty_OpenFile.Close() > > pty_BufReader := bufio.NewReader(pty_OpenFile) > sl_Output := make([]byte, BufSize) > > _, err = pty_BufReader.Read(sl_Output) > if err != nil { > return "", err > } else { > return string(sl_Output), nil > } > > } > > func fc_ClearFile(FilePathInput string) error { > > _, err := os.OpenFile(FilePathInput, os.O_WRONLY|os.O_TRUNC, 0222) > return err > } > > func fc_AppendFile(FilePathInput string, WriteString string) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_WRONLY|os.O_APPEND, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > pty_NewWriter := bufio.NewWriter(pty_OpenFile) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_AppendBufFile(FilePathInput string, WriteString string, vi_Bufsize int) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_WRONLY|os.O_APPEND, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > //使用NewWriter方法返回的io.Writer緩衝預設大小為4096,也可以使用NewWriterSize方法設定快取的大小 > pty_NewWriter := bufio.NewWriterSize(pty_OpenFile, vi_Bufsize) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_WriteNewFile(FilePathInput string, WriteString string) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_CREATE|os.O_RDWR, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > pty_NewWriter := bufio.NewWriter(pty_OpenFile) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_WriteBufNewFile(FilePathInput string, WriteString string, vi_Bufsize int) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_CREATE|os.O_RDWR, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > //使用NewWriter方法返回的io.Writer緩衝預設大小為4096,也可以使用NewWriterSize方法設定快取的大小 > pty_NewWriter := bufio.NewWriterSize(pty_OpenFile, vi_Bufsize) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_UrlDownload(SavePathInput string, UrlInput string) error { > > // Get the data > pty_HttpRESP, err := http.Get(UrlInput) > if err != nil { > return err > } > defer pty_HttpRESP.Body.Close() > > // Create the file > pty_FileOut, err := os.Create(SavePathInput) > if err != nil { > return err > } > defer pty_FileOut.Close() > > // Write the body to file > _, err = io.Copy(pty_FileOut, pty_HttpRESP.Body) > return err > } > > ``` > > ``FileMode:`` > | 函式名稱 | 說明 | > | - | - | > | O_RDONLY | 只讀模式打開文件 | > | O_WRONLY | 只寫模式打開文件 | > | O_RDWR | 讀寫模式打開文件 | > | O_APPEND | 寫操作時將數據附加到文件尾部 | > | O_CREAT | 如果不存在將創建一個新文件 | > | O_EXCL | 和O_CREATE配合使用,文件必須不存在 | > | O_SYNC | 打開文件用於同步I/O | > | O_TRUNC | 如果可能,打開時清空文件 | > ### Go : 參數命令列(os&flag)應用: > ```go= > package main > > import ( > "flag" > "fmt" > "os" > ) > > func main() { > > //os.Args為string切片類型 > fmt.Printf("一共接收到: %v 個參數 (%T)\n", len(os.Args), os.Args) > > for index, value := range os.Args { > fmt.Printf("os.Args[%v]=%v\n", index, value) > } //os.Args命令列參數具備次序性 > > fmt.Println() > fmt.Println() > //////////////////////////////// > > var user string > var pwd string > var port int > > flag.StringVar(&user, "u", "預設用戶名", "用戶名") > flag.StringVar(&pwd, "pwd", "", "密碼") > flag.IntVar(&port, "port", 0, "Port") > > flag.Parse() //flag解析命令列參數 > > fmt.Printf("User=%v\nPwd=%v\nPort=%v\n", user, pwd, port) > > // cmd.exe -u 123 -pwd "密碼測試" -port 00 > // 一共接收到: 7 個參數 ([]string) > // os.Args[0]=cmd.exe > // os.Args[1]=-u > // os.Args[2]=123 > // os.Args[3]=-pwd > // os.Args[4]=密碼測試 > // os.Args[5]=-port > // os.Args[6]=00 > > // User=123 > // Pwd=密碼測試 > // Port=0 > > } > ``` > ### Go : 常用資料格式(json)應用: > ``json.Marshal`` > ```go= > package main > > import ( > "encoding/json" > "fmt" > ) > > func main() { > > ////////////////////////////////////////////////////////////////// > // 將結構體輸出成json格式 > type stMonster struct { > Name string //json包輸入變數必須大寫 > Age int > Weight float64 > } > > var oMonster = stMonster{ > Name: "名稱", > Age: 99, > } > > vData, err := json.Marshal(&oMonster) > if err != nil { > fmt.Printf("json.Marshal錯誤 : %v\n", err) > } else { > fmt.Printf("json.Marshal解析結果 : %v\n", string(vData)) > } > // json.Marshal解析結果 : {"Name":"名稱","Age":99,"Weight":0} > ///////////////////////////////////////////////////////////////// > > ////////////////////////////////////////////////////////////////// > // 將結構體輸出成json格式 > type stMonsterJ struct { > Name string `json:"json名稱"` > Age int `json:"json年齡"` > Weight float64 `json:"json重量"` > } > > var oMonsterJ = stMonsterJ{ > Name: "JJJJ", > Age: 00, > Weight: 0.0, > } > > vData, err = json.Marshal(&oMonsterJ) > if err != nil { > fmt.Printf("json.Marshal錯誤 : %v\n", err) > } else { > fmt.Printf("json.Marshal解析結果 : %v\n", string(vData)) > } > //json.Marshal解析結果 : {"json名稱":"JJJJ","json年齡":0,"json重量":0} > ///////////////////////////////////////////////////////////////// > > ///////////////////////////////////////////////////////////////// > // 將map輸出成json格式 > var mData map[string]interface{} > mData = make(map[string]interface{}) > mData["Name"] = "名稱測試" > mData["Age"] = 18 > mData["Weight"] = 99.0 > > vData, err = json.Marshal(&mData) > if err != nil { > fmt.Printf("json.Marshal錯誤 : %v\n", err) > } else { > fmt.Printf("json.Marshal解析結果 : %v\n", string(vData)) > } > //json.Marshal解析結果 : {"Age":18,"Name":"名稱測試","Weight":99} > ///////////////////////////////////////////////////////////////// > > ///////////////////////////////////////////////////////////////// > // 將切片加入空接口並寫入map元素輸出成json格式 > var sSlice []map[string]interface{} > > var mData1 map[string]interface{} > mData1 = make(map[string]interface{}) > mData1["Name"] = "名稱測試" > mData1["Age"] = 18 > mData1["Weight"] = 99.0 > > var mData2 map[string]interface{} > mData2 = make(map[string]interface{}) > mData2["Name"] = "名稱測試" > mData2["Age"] = 18 > mData2["Weight"] = 99.0 > > sSlice = append(sSlice, mData1, mData2) > > vData, err = json.Marshal(sSlice) > if err != nil { > fmt.Printf("json.Marshal錯誤 : %v\n", err) > } else { > fmt.Printf("json.Marshal解析結果 : %v\n", string(vData)) > } > //json.Marshal解析結果 : [{"Age":18,"Name":"名稱測試","Weight":99},{"Age":18,"Name":"名稱測試","Weight":99}] > ///////////////////////////////////////////////////////////////// > > } > > ``` > > ``json.Unmarshal`` > ```go= > package main > > import ( > "encoding/json" > "fmt" > ) > > func main() { > var err error > > sSlice := []byte(`{"Name":"柏崎星奈","Age":16,"Married":false}`) > // interface 可以接收任何類型的值 > var mMap map[string]interface{} > err = json.Unmarshal(sSlice, &mMap) //Unmarshal只能接收 > if err != nil { > panic(err) > } > fmt.Println("Name: ", mMap["Name"]) > fmt.Println("Age: ", mMap["Age"]) > fmt.Printf("Married: %v\n", mMap["Married"].(bool)) // interface 也可以直接轉成你要的型別 > fmt.Println() > > // Name: 柏崎星奈 > // Age: 16 > // Married: false > > type Identification struct { > Phone string `json:"JPhone"` > Email string `json:"JEmail"` > } > > type User struct { > ID int > Name string > Skills []string > Relationship map[string]string > Identification Identification > } > > var sJsonBlob = []byte(`{"錯誤":"雜訊","Name":"人員名稱","Skills":["玩","運動"],"Relationship":{"Dad":"Hulk","Mon":"Natasha"},"Identification":{"JPhone":"cc@cc.com","JEmail":false}}`) > var stUser User > err = json.Unmarshal(sJsonBlob, &stUser) > if err != nil { > fmt.Println("error:", err) > } > fmt.Printf("%+v", stUser) > fmt.Println() > // error: json: cannot unmarshal bool into Go struct field Identification.JEmail of type string //該錯誤為 "JEmail":false 所產生 > // {ID:0 Name:人員名稱 Skills:[玩 運動] Relationship:map[Dad:Hulk Mon:Natasha] Identification:{Phone:cc@cc.com Email:}} > // 多餘的切片元素將會被json.Unmarshal棄置 > } > > ``` ### Go : 注入(inject)(github)應用: > ``go get github.com/codegangsta/inject`` > ```go= > package main > import ( > "fmt" > > "github.com/codegangsta/inject" > ) > > /* > github.com/codegangsta/inject 利用反射(reflect)包所設計, > 可將自動將變數注入到func和struct中,實現變數自動排列輸入 > > */ > > type TY_S1 interface{} > type TY_S2 interface{} > > type Ty_Staff struct { > Name string `inject` > Company TY_S1 `inject` > Level TY_S2 `inject` > Age int `inject` > } > > func FC_PrintData(vs_name string, if_company TY_S1, if_level TY_S2, vi_age int) { > fmt.Printf("name=%s, company=%s, level=%s, age=%d\n", vs_name, if_company, if_level, vi_age) > } > > func main() { > > InjectNew := inject.New() > InjectNew.Map("姓名") > InjectNew.MapTo("公司名稱", (*TY_S1)(nil)) > InjectNew.MapTo("職稱", (*TY_S2)(nil)) > InjectNew.Map(23) > InjectNew.Invoke(FC_PrintData) //name=姓名, company=公司名稱, level=職稱, age=99 > > st_Staff := Ty_Staff{} > InjectNew.Apply(&st_Staff) > fmt.Printf("%v\n", st_Staff) //{XX 公司名稱 職稱 99} > > println() > ///////////////////////////////////////////////////////////////////////////////////////////////////////// > > InjectNewE := inject.New() > InjectNewE.Map("姓名") > InjectNewE.MapTo("公司名稱", (*TY_S1)(nil)) > InjectNewE.MapTo("職稱", (*TY_S2)(nil)) > InjectNewE.Map(23) > > InjectNewE.Map("覆寫姓名") > InjectNewE.MapTo("覆寫公司名稱", (*TY_S1)(nil)) > InjectNewE.MapTo("覆寫職稱", (*TY_S2)(nil)) > InjectNewE.Map(99) > InjectNewE.Map(11.11) //多餘資料 > > InjectNewE.Invoke(FC_PrintData) //name=覆寫姓名, company=覆寫公司名稱, level=覆寫職稱, age=99 > > st_StaffE := Ty_Staff{} > InjectNewE.Apply(&st_StaffE) > fmt.Printf("%v\n", st_StaffE) //{覆寫姓名 覆寫公司名稱 覆寫職稱 99} > > } > ``` ### Go : rpc應用: > ==server.go== > ```go= > package main > > import ( > "log" > "net" > "net/http" > "net/rpc" > ) > > type Item struct { > Title string > Body string > } > > type API int > > var database []Item > > func (a *API) GetDB(empty string, reply *[]Item) error { > *reply = database > return nil > } > > func (a *API) GetByName(title string, reply *Item) error { > var getItem Item > > for _, val := range database { > if val.Title == title { > getItem = val > } > } > > *reply = getItem > > return nil > } > > func (a *API) AddItem(item Item, reply *Item) error { > database = append(database, item) > *reply = item > return nil > } > > func (a *API) EditItem(item Item, reply *Item) error { > var changed Item > > for idx, val := range database { > if val.Title == item.Title { > database[idx] = Item{item.Title, item.Body} > changed = database[idx] > } > } > > *reply = changed > return nil > } > > func (a *API) DeleteItem(item Item, reply *Item) error { > var del Item > > for idx, val := range database { > if val.Title == item.Title && val.Body == item.Body { > database = append(database[:idx], database[idx+1:]...) > del = item > break > } > } > > *reply = del > return nil > } > > func main() { > api := new(API) > err := rpc.Register(api) > if err != nil { > log.Fatal("error registering API", err) > } > > rpc.HandleHTTP() > > listener, err := net.Listen("tcp", ":4040") > > if err != nil { > log.Fatal("Listener error", err) > } > log.Printf("serving rpc on port %d", 4040) > http.Serve(listener, nil) > > if err != nil { > log.Fatal("error serving: ", err) > } > > > } > ``` > > ==client.go== > ```go= > package main > > import ( > "fmt" > "log" > "net/rpc" > ) > > type Item struct { > Title string > Body string > } > > func main() { > var reply Item > var db []Item > > client, err := rpc.DialHTTP("tcp", "localhost:4040") > > if err != nil { > log.Fatal("Connection error: ", err) > } > > a := Item{"First", "A first item"} > b := Item{"Second", "A second item"} > c := Item{"Third", "A third item"} > > client.Call("API.AddItem", a, &reply) > client.Call("API.AddItem", b, &reply) > client.Call("API.AddItem", c, &reply) > client.Call("API.GetDB", "", &db) > > fmt.Println("Database: ", db) > > client.Call("API.EditItem", Item{"Second", "A new second item"}, &reply) > > client.Call("API.DeleteItem", c, &reply) > client.Call("API.GetDB", "", &db) > fmt.Println("Database: ", db) > > client.Call("API.GetByName", "First", &reply) > fmt.Println("first item: ", reply) > > } > > ``` ### Go : Protobuf (proto3)應用: > > [參考資料](https://yami.io/protobuf/) > [變數型別(proto3)](https://developers.google.com/protocol-buffers/docs/proto3) > > ```bash > ##Golang proto套件安裝 > go get -u github.com/golang/protobuf/{proto,protoc-gen-go} > ``` > > ```bash > # Ubuntu安裝 > curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.10.1/protoc-3.10.1-linux-x86_64.zip > su > unzip protoc-3.10.1-linux-x86_64.zip -d protoc3 > > # 移動 > mv protoc3/bin/* /usr/local/bin/ > mv protoc3/include/* /usr/local/include/ > > # 修改權限 > chown [user] /usr/local/bin/protoc > chown -R [user] /usr/local/include/google > ``` > > ```bash > ## windows安裝protoc後必須另外將protoc.exe環境變數%Path%內 > ## protoc-gen-go.exe必須與protoc.exe在同一資料夾 > copy protoc-gen-go.exe %Path% > ``` > Golang範例: > > ``./person.proto``: > ``` go > syntax="proto3"; > > package main; > > message Person { > string name = 1; > int32 age = 2; > } > ``` > > ```bash > ## 將person.proto生成 person.pb.go > protoc --go_out=. person.proto > ``` > > ``./person.pb.go``: > ```go= > // Code generated by protoc-gen-go. DO NOT EDIT. > // source: person.proto > > package main > > import ( > fmt "fmt" > math "math" > > proto "github.com/golang/protobuf/proto" > ) > > // Reference imports to suppress errors if they are not otherwise used. > var _ = proto.Marshal > var _ = fmt.Errorf > var _ = math.Inf > > // This is a compile-time assertion to ensure that this generated file > // is compatible with the proto package it is being compiled against. > // A compilation error at this line likely means your copy of the > // proto package needs to be updated. > const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package > > type Person struct { > Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"Name,omitempty"` > Age int32 `protobuf:"varint,3,opt,name=age,proto3" json:"age,omitempty"` > XXX_NoUnkeyedLiteral struct{} `json:"-"` > XXX_unrecognized []byte `json:"-"` > XXX_sizecache int32 `json:"-"` > } > > func (m *Person) Reset() { *m = Person{} } > func (m *Person) String() string { return proto.CompactTextString(m) } > func (*Person) ProtoMessage() {} > func (*Person) Descriptor() ([]byte, []int) { > return fileDescriptor_4c9e10cf24b1156d, []int{0} > } > > func (m *Person) XXX_Unmarshal(b []byte) error { > return xxx_messageInfo_Person.Unmarshal(m, b) > } > func (m *Person) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { > return xxx_messageInfo_Person.Marshal(b, m, deterministic) > } > func (m *Person) XXX_Merge(src proto.Message) { > xxx_messageInfo_Person.Merge(m, src) > } > func (m *Person) XXX_Size() int { > return xxx_messageInfo_Person.Size(m) > } > func (m *Person) XXX_DiscardUnknown() { > xxx_messageInfo_Person.DiscardUnknown(m) > } > > var xxx_messageInfo_Person proto.InternalMessageInfo > > func (m *Person) GetName() string { > if m != nil { > return m.Name > } > return "" > } > > func (m *Person) GetAge() int32 { > if m != nil { > return m.Age > } > return 0 > } > > func init() { > proto.RegisterType((*Person)(nil), "main.Person") > } > > func init() { proto.RegisterFile("person.proto", fileDescriptor_4c9e10cf24b1156d) } > > var fileDescriptor_4c9e10cf24b1156d = []byte{ > // 88 bytes of a gzipped FileDescriptorProto > 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x48, 0x2d, 0x2a, > 0xce, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc, 0x53, 0xd2, > 0xe3, 0x62, 0x0b, 0x00, 0x8b, 0x0a, 0x09, 0x71, 0xb1, 0xf8, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x29, > 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x02, 0x5c, 0xcc, 0x89, 0xe9, 0xa9, 0x12, 0xcc, 0x0a, > 0x8c, 0x1a, 0xac, 0x41, 0x20, 0x66, 0x12, 0x1b, 0x58, 0xb3, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, > 0xf8, 0xbc, 0xb8, 0xfe, 0x4c, 0x00, 0x00, 0x00, > } > > > ``` > > ``./main.go``: > ```go > package main > > import ( > "fmt" > > "github.com/golang/protobuf/proto" > ) > > func main() { > > elliot := &Person{ > Name: "Elliot", > Age: 24, > } > > sl_ProtobufExample, err := proto.Marshal(elliot) > if err != nil { > fmt.Println("marshaling error: ", err) > } > fmt.Printf("%s\n", sl_ProtobufExample) //?Elliot > > st_UnProtoExample := &Person{} > err = proto.Unmarshal(sl_ProtobufExample, st_UnProtoExample) > if err != nil { > fmt.Println("unmarshaling error: ", err) > } > > fmt.Println(st_UnProtoExample) //name:"Elliot" age:24 > > } > > ``` > ### Go : 檔案系統操作: > ```go= > package main > > import ( > "bufio" > "errors" > "io" > "io/ioutil" > "net/http" > "os" > "strconv" > ) > > // os.O_WRONLY: 只寫 > // os.O_CREATE: 創建文件(如果文件不存在) > // os.O_RDONLY: 只讀 > // os.O_RDWR: 讀寫皆可 > // os.O_APPEND: 尾部加入 > // os.O_TRUNC: 清空(文件存在情況下) > // 第三個參數設置權限控制 > > > > func fc_GetPWD() string { > vs_WorkingDir, _ := os.Getwd() > return vs_WorkingDir > } > > func fc_SetPWD(FilePathInput string) error { > err := os.Chdir(FilePathInput) > return err > } > > func fc_GetEnv(EnvVarName string) (EnvVarValue string) { > return os.Getenv(EnvVarName) > } > > func fc_GetAllEnv(EnvVarName string) (AllEnvVarValues []string) { > return os.Environ() > } > > func fc_GetPathList(FilePathInput string) (FolderList []string, err error) { > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return FolderList, err > } > > ty_FileInfos, err := pty_OpenFile.Readdir(-1) > pty_OpenFile.Close() > if err != nil { > return FolderList, err > } > for _, value := range ty_FileInfos { > FolderList = append(FolderList, value.Name()) > } > return FolderList, nil > } > > func fc_GetFolderList(FilePathInput string) (FolderList []string, err error) { > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return FolderList, err > } > > ty_FileInfos, err := pty_OpenFile.Readdir(-1) > pty_OpenFile.Close() > if err != nil { > return FolderList, err > } > for _, value := range ty_FileInfos { > if value.IsDir(){ > FolderList = append(FolderList, value.Name()) > } > } > return FolderList, nil > } > > func fc_GetFileList(FilePathInput string) (FolderList []string, err error) { > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return FolderList, err > } > > ty_FileInfos, err := pty_OpenFile.Readdir(-1) > pty_OpenFile.Close() > if err != nil { > return FolderList, err > } > for _, value := range ty_FileInfos { > if !value.IsDir(){ > FolderList = append(FolderList, value.Name()) > } > } > return FolderList, nil > } > > func fc_FileMove(FilePathInput, FilePathOutput string) error { > return os.Rename(FilePathInput, FilePathOutput) > } > > func fc_SetEnv(EnvVarName, EnvVarValue string) error { > return os.Setenv(EnvVarName, EnvVarValue) > } > > func fc_IsFileModeCode(vs_chmod string) bool { > if len(vs_chmod) == 3 || len(vs_chmod) == 4 { > _, err_chmodCheck := strconv.ParseUint(vs_chmod, 10, 32) > if err_chmodCheck == nil { > for _, value := range vs_chmod { > if value == '8' || value == '9' { > return false > } > } > return true > } > } > return false > } > > func fc_FileModeEnCode(vs_chmod string) (vs_ModeCodestring string, err error) { > switch len(vs_chmod) { > case 1: //r、w表示同時適用OWNER(Windows,Unix) > switch vs_chmod { > case "r": > return "0200", nil > case "w": > return "0400", nil > } > case 2: //rw兩字表示同時適用OWNER(Windows,Unix) > if vs_chmod == "rx" || vs_chmod == "xr" { > return "0200", nil > } else if vs_chmod == "wx" || vs_chmod == "xw" { > return "0400", nil > } else if vs_chmod == "rw" || vs_chmod == "wr" { > return "0600", nil > } > case 3: //rwx三字表示同時適用OWNER(Windows,Unix),777三數字僅適用Unix > switch vs_chmod { > case "rwx", "rxw", "xrw", "xwr", "wrx", "wxr": > return "0700", nil > default: > if fc_IsFileModeCode(vs_chmod) { > return "0" + vs_chmod, nil > } > } > case 4: > if fc_IsFileModeCode(vs_chmod) { > return vs_chmod, nil > } > case 9: > var vu_ower, vu_group, vu_others, vu_sum uint64 > for index, value := range vs_chmod { > switch value { > case 'r': > vu_sum = vu_sum + 4 > case 'w': > vu_sum = vu_sum + 2 > case 'x': > vu_sum = vu_sum + 1 > case '-': > continue > default: > return "", errors.New("Bad chmod input") > } > > switch index { > case 2: > vu_ower = vu_sum > vu_sum = 0 > case 5: > vu_group = vu_sum > vu_sum = 0 > case 8: > vu_others = vu_sum > case 9: > return "", errors.New("Bad chmod input") > } > } > > if vu_ower > 7 || vu_group > 7 || vu_others > 7 { > return "", errors.New("Bad chmod input") > } else { > return "0" + strconv.FormatUint(vu_ower, 10) + strconv.FormatUint(vu_group, 10) + strconv.FormatUint(vu_others, 10), nil > } > > } > return "", errors.New("Bad chmod input") > } > > func fc_IsExist(FilePathInput string) bool { > if _, err := os.Stat(FilePathInput); os.IsExist(err) { > return false > } > return true > } > > func fc_IsNotExist(FilePathInput string) bool { > if _, err := os.Stat(FilePathInput); os.IsNotExist(err) { > return true > } > return false > } > > func fc_CreatFile(FilePathInput string) error { > _, err := os.Create(FilePathInput) //666 > return err > } > > func fc_CreatFolder(FilePathInput, vs_chmod string) error { > var err error > if vs_chmod == "" { > err = os.MkdirAll(FilePathInput, 0777) > return err > } > > vu_chmodNum, err_chmodCheck := strconv.ParseUint(vs_chmod, 10, 32) > if err_chmodCheck != nil { > err = os.MkdirAll(FilePathInput, 0777) > } else { > err = os.MkdirAll(FilePathInput, os.FileMode(vu_chmodNum)) > } > > return err > } > > func fc_RemovePath(FilePathInput string) error { > err := os.RemoveAll(FilePathInput) > return err > } > > func fc_RemoveFile(FilePathInput string) error { > ty_FileStat, err := os.Stat(FilePathInput) > if !ty_FileStat.IsDir() { > err = os.Remove(FilePathInput) > return err > } else { > return errors.New("It's a Folder not File") > } > } > > func fc_RemoveFolder(FilePathInput string) error { > ty_FileStat, err := os.Stat(FilePathInput) > if ty_FileStat.IsDir() { > err = os.Remove(FilePathInput) > return err > } else { > return errors.New("It's a File not Empty-Folder") > } > } > > func fc_ReadFile(FilePathInput string) (string, error) { > > sl_Output, err := ioutil.ReadFile(FilePathInput) > if err != nil { > return "", err > } else { > return string(sl_Output), nil > } > > } > > func fc_ReadBufFile(FilePathInput string, BufSize int64) (string, error) { > > pty_OpenFile, err := os.Open(FilePathInput) > if err != nil { > return "", err > } > defer pty_OpenFile.Close() > > pty_BufReader := bufio.NewReader(pty_OpenFile) > sl_Output := make([]byte, BufSize) > > _, err = pty_BufReader.Read(sl_Output) > if err != nil { > return "", err > } else { > return string(sl_Output), nil > } > > } > > func fc_ClearFile(FilePathInput string) error { > > _, err := os.OpenFile(FilePathInput, os.O_WRONLY|os.O_TRUNC, 0222) > return err > } > > func fc_AppendFile(FilePathInput string, WriteString string) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_WRONLY|os.O_APPEND, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > pty_NewWriter := bufio.NewWriter(pty_OpenFile) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_AppendBufFile(FilePathInput string, WriteString string, vi_Bufsize int) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_WRONLY|os.O_APPEND, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > //使用NewWriter方法返回的io.Writer緩衝預設大小為4096,也可以使用NewWriterSize方法設定快取的大小 > pty_NewWriter := bufio.NewWriterSize(pty_OpenFile, vi_Bufsize) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_WriteNewFile(FilePathInput string, WriteString string) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_CREATE|os.O_RDWR, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > pty_NewWriter := bufio.NewWriter(pty_OpenFile) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_WriteBufNewFile(FilePathInput string, WriteString string, vi_Bufsize int) error { > pty_OpenFile, err := os.OpenFile(FilePathInput, os.O_CREATE|os.O_RDWR, 0222) > if err != nil { > return err > } > defer pty_OpenFile.Close() > > //使用NewWriter方法返回的io.Writer緩衝預設大小為4096,也可以使用NewWriterSize方法設定快取的大小 > pty_NewWriter := bufio.NewWriterSize(pty_OpenFile, vi_Bufsize) > > //將檔案寫入快取 > if _, err = pty_NewWriter.WriteString(WriteString); err != nil { > return err > } > //從快取寫入到檔案中 > if err = pty_NewWriter.Flush(); err != nil { > return err > } else { > return nil > } > } > > func fc_UrlDownload(SavePathInput string, UrlInput string) error { > > // Get the data > pty_HttpRESP, err := http.Get(UrlInput) > if err != nil { > return err > } > defer pty_HttpRESP.Body.Close() > > // Create the file > pty_FileOut, err := os.Create(SavePathInput) > if err != nil { > return err > } > defer pty_FileOut.Close() > > // Write the body to file > _, err = io.Copy(pty_FileOut, pty_HttpRESP.Body) > return err > } > > > ``` ### Go : try-catch 實作: > ```go= > package main > > import "fmt" > > func main() { > > someVar := "----- start test -----" > emptyVar := "" > > Catcher(func(panicErr interface{}) { > > fmt.Printf("something error : \n%v\n", panicErr) > > }, func() { > > fmt.Println(someVar) > //panic("自定義錯誤") > fmt.Println(emptyVar[0]) > > }) > > fmt.Println("----- still running -----") > > // Try func active > // ----- start test ----- > // something error : > // runtime error: index out of range > // ----- still running ----- > } > > func Catcher(handler func(interface{}), run func()) { > defer func() { > if err := recover(); err != nil { > handler(err) > } > }() > > fmt.Println("Try func active") > run() > } > > ``` ### Go : Google Sheet API對接實作: > ```go= > package main > > import ( > "bytes" > "encoding/json" > "errors" > "fmt" > "io/ioutil" > "log" > "os" > > "golang.org/x/net/context" > "golang.org/x/oauth2" > "golang.org/x/oauth2/google" > "google.golang.org/api/sheets/v4" > ) > > const ( > googleAppCredentials = "./license/credentials.json" > googleAccountToken = "./license/token.json" > googleErrorTitle = "Google Sheet API 預期錯誤" > ) > > func main() { > > credentials, err := ioutil.ReadFile(googleAppCredentials) > if err != nil { > log.Fatalf("Unable to read client secret file: %v\n", err) > //https://developers.google.com/sheets/api/quickstart/go 需要開啟獲取app 使用google api許可 > } > > token, err := ioutil.ReadFile(googleAccountToken) > if err != nil { > log.Printf("Unable to read client secret file: %v\n", err) > > var getGSService GoogleSheet > token, err = getGSService.CreatAccTokenFromWeb(credentials, googleAccountToken, false) > if err != nil { > log.Fatalf("Unable to save oauth token: \n %s (%v)\n", credentials, err) > } > > } else { > var js map[string]interface{} //空interface可以容許json格式多餘換行,較人性化 > if json.Unmarshal(token, &js) != nil { > log.Fatalf("INVALID json: %v\n%v", err, token) > //log.Printf("INVALID json: %v\n%v\n進行重新獲取Token>>>>>\n", err, token) > } > } > > // var gs1 GoogleSheet > // gs1.SetService(credentials, token, "1OQmB9EOXTw07nytdlQeEtvhmREn2-koSc5rlIhNLHHc", false) > > // values, err := gs1.Read("Table!A2:B9") > // if err != nil { > // log.Fatalf("Unable to retrieve data from sheet: %v\n", err) > // } > // fmt.Println(values) > > // var writeValues [][]interface{} > // xx := []interface{}{ > // 0, 1, 2, 3, 4, 5, > // 10, 11, 12, 13, 14, 15, > // 20, 21, 22, 23, 24, 25, > // 30, 31, 32, 33, 34, 35, > // } > // writeValues = append(writeValues, xx) > > // err = gs1.Clear("Table!C8:C9") > // if err != nil { > // log.Fatalf("Unable to retrieve data from sheet: %v\n", err) > // } > > // var updateValues [][]interface{} > // row := []interface{}{"BBB", "CCC", "DDD"} > // updateValues = append(updateValues, row) > // err = gs1.Update("Table!C3", updateValues) > // if err != nil { > // log.Fatal(err) > // } > > var gs1 GoogleSheet > gs1.SetService(credentials, token, "1OQmB9EOXTw07nytdlQeEtvhmREn2-koSc5rlIhNLHHc", false) > //gs1.SheetCopy("1OQmB9EOXTw07nytdlQeEtvhmREn2-koSc5rlIhNLHHc", 210732483, "161anr6Guf9Mdmcz--10bb9G62aEEgbjQGJjzJXMqVRY") > gs1.CopyFromSheet("161anr6Guf9Mdmcz--10bb9G62aEEgbjQGJjzJXMqVRY", 2014644818, "test") > > //fmt.Println(gs1.GetSheetGID("「Monthly Rates」的副本")) > //gs1.SheetRename(1791097333,"測試!!!!!!!!!!!!!!") > > } > > type GoogleSheet struct { > Srv *sheets.Service > Ctx context.Context > AppCredentials []byte //程式本身許可license > AccToken []byte //google帳戶license > SpreadSheetID string //預設google Sheet ID (由網址url取得) > } > > //獲取google API 控制權限設定(綁定method) > func (gs *GoogleSheet) getGoogleAPIConfig(appCredentials []byte, setReadOnly bool) (config *oauth2.Config, err error) { > // https://developers.google.com/sheets/api/guides/authorizing 選擇api控制google帳號權限 > // https://www.googleapis.com/auth/spreadsheets.readonly Allows read-only access to the user's sheets and their properties. > // https://www.googleapis.com/auth/spreadsheets Allows read/write access to the user's sheets and their properties. > // https://www.googleapis.com/auth/drive.readonly Allows read-only access to the user's file metadata and file content. > // https://www.googleapis.com/auth/drive.file Per-file access to files created or opened by the app. > // https://www.googleapis.com/auth/drive > var scopeUrl string > if setReadOnly { > scopeUrl = sheets.SpreadsheetsReadonlyScope > } else { > scopeUrl = sheets.SpreadsheetsScope > } > > config, err = google.ConfigFromJSON(appCredentials, scopeUrl) > if err != nil { > //log.Printf("Unable to parse client secret file to config: %v\n", err) > return nil, errors.New(googleErrorTitle + ": AppCredentials 內容格式錯誤 !") > } > > return config, nil > } > > func (gs *GoogleSheet) GetSheetGID(sheetName string) (int64, error) { > > sheetService, err := gs.Srv.Spreadsheets.Get(gs.SpreadSheetID).Do() > if err != nil { > return 0, errors.New(googleErrorTitle + ": 「" + gs.SpreadSheetID + "」 該SheetID不存在,請檢查網址或AccountToken權限!") > } > > for _, sheet := range sheetService.Sheets { > if sheet.Properties.Title == sheetName { > return sheet.Properties.SheetId, nil > } > } > return 0, errors.New(googleErrorTitle + ": 「" + sheetName + "」 該Sheet Name不存在,請檢查Sheet內容!") > } > > //建立Google帳戶Token File(需要瀏覽器手動操作) > func (gs *GoogleSheet) CreatAccTokenFromWeb(appCredentials []byte, accTokenFilePath string, setReadOnly bool) (accToken []byte, err error) { > > config, err := gs.getGoogleAPIConfig(appCredentials, setReadOnly) > if err != nil { > //log.Printf("Unable to parse client secret file to config: %v\n", err) > return nil, errors.New(googleErrorTitle + ": AppCredentials 內容格式錯誤 !") > } > > // Request a token from the web, then returns the retrieved token. > authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline) > fmt.Printf("\n%v\n↑請使用瀏覽器設定你的Google帳戶,並貼入你的token authorization code ↑\n", authURL) > var authCode string > if _, err := fmt.Scan(&authCode); err != nil { > //log.Fatalf("Unable to read authorization code: %v", err) > return nil, err > } > > tok, err := config.Exchange(context.TODO(), authCode) > if err != nil { > //log.Fatalf("Unable to retrieve token from web: %v", err) > return nil, err > } > > // Saves a token to a file path. > fmt.Printf("已將token authorization code存入 : %s\n", accTokenFilePath) > accTokenFileWrite, err := os.OpenFile(accTokenFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) > if err != nil { > //log.Fatalf("Unable to cache oauth token: %v", err) > return nil, errors.New(googleErrorTitle + ": 「" + accTokenFilePath + "」AccountToken儲存失敗!") > } > defer accTokenFileWrite.Close() > json.NewEncoder(accTokenFileWrite).Encode(tok) > > gs.Ctx = context.Background() > client := config.Client(gs.Ctx, tok) > _, err = sheets.New(client) > if err != nil { > //log.Fatalf("Unable to retrieve Sheets client: %v", err) > return nil, err > } > > token, err := ioutil.ReadFile(accTokenFilePath) > if err != nil { > //log.Fatalf("Unable to save oauth token: %v", err) > return nil, err > } > > return []byte(token), nil > } > > //設置Google Sheet連結服務 > func (gs *GoogleSheet) SetService(appCredentials []byte, accToken []byte, spreadSheetID string, setReadOnly bool) (*sheets.Service, error) { > > gs.AppCredentials = appCredentials > gs.AccToken = accToken > gs.SpreadSheetID = spreadSheetID //預先存入docs網址方便日後存取 > > config, err := gs.getGoogleAPIConfig(appCredentials, setReadOnly) > if err != nil { > // log.Printf("Unable to parse client secret file to config: %v\n", err) > return nil, errors.New(googleErrorTitle + ": AppCredentials 內容格式錯誤 !") > } > > tok := &oauth2.Token{} > err = json.NewDecoder(bytes.NewReader(gs.AccToken)).Decode(tok) > if err != nil { > //log.Fatalf("Unable to cache oauth token: %v", err) > return nil, err > } > client := config.Client(context.Background(), tok) > > gs.Srv, err = sheets.New(client) > if err != nil { > //log.Printf("Unable to retrieve Sheets client: %v\n", err) > return nil, err > } > > return gs.Srv, nil > > } > > //獲取sheet中的數值 > func (gs *GoogleSheet) SheetRead(readRange string) ([][]interface{}, error) { > resp, err := gs.Srv.Spreadsheets.Values.Get(gs.SpreadSheetID, readRange).Do() > > if err != nil { > return nil, err > } > > return resp.Values, err > } > > //寫入sheet中的數值 > func (gs *GoogleSheet) SheetWrite(writeRange string, values [][]interface{}) error { > valueInputOption := "RAW" > requestBody := &sheets.ValueRange{ > MajorDimension: "ROWS", > Values: values, > } > > _, err := gs.Srv.Spreadsheets.Values.Append(gs.SpreadSheetID, writeRange, requestBody).ValueInputOption(valueInputOption).Do() > > return err > } > > //改變sheet中的數值 > func (gs *GoogleSheet) SheetUpdate(updateRange string, updateValues [][]interface{}) error { > valueInputOption := "RAW" > requestBody := &sheets.ValueRange{ > MajorDimension: "ROWS", > Values: updateValues, > } > > _, err := gs.Srv.Spreadsheets.Values.Update(gs.SpreadSheetID, updateRange, requestBody).ValueInputOption(valueInputOption).Do() > > return err > } > > //清除sheet中的數值 > func (gs *GoogleSheet) SheetClear(clearRange string) error { > // rb has type *ClearValuesRequest > requestBody := &sheets.ClearValuesRequest{} > > _, err := gs.Srv.Spreadsheets.Values.Clear(gs.SpreadSheetID, clearRange, requestBody).Do() > > return err > } > > //操作兩Google Sheet 間作sheet複製(需要自行設置權限,並取得Sheet的gid) > func (gs *GoogleSheet) CopyBetweenSheet(sourceSheetID string, sourceSheetGID int64, destSheetID string) (resp *sheets.SheetProperties, err error) { > > requestBody := &sheets.CopySheetToAnotherSpreadsheetRequest{ > DestinationSpreadsheetId: destSheetID, > } > > resp, err = gs.Srv.Spreadsheets.Sheets.CopyTo(sourceSheetID, sourceSheetGID, requestBody).Context(gs.Ctx).Do() > > return resp, err > } > > //選擇sheetGID 並重新命名 > func (gs *GoogleSheet) SheetGIDRename(sheetGID int64, newName string) (resp *sheets.BatchUpdateSpreadsheetResponse, err error) { > > requestBody := &sheets.BatchUpdateSpreadsheetRequest{ > Requests: []*sheets.Request{&sheets.Request{ > UpdateSheetProperties: &sheets.UpdateSheetPropertiesRequest{ > Properties: &sheets.SheetProperties{ > SheetId: sheetGID, > Title: newName, > }, > Fields: "title", > }, > }}, > } > > resp, err = gs.Srv.Spreadsheets.BatchUpdate(gs.SpreadSheetID, requestBody).Context(gs.Ctx).Do() > return resp, err > } > > //複製外部sheet到現在工作的Google Sheet(需要自行設置權限,並取得Sheet的gid) > func (gs *GoogleSheet) CopyFromSheet(sourceSheetID string, sourceSheetGID int64, newName string) (resp *sheets.SheetProperties, err error) { > > requestBody := &sheets.CopySheetToAnotherSpreadsheetRequest{ > DestinationSpreadsheetId: gs.SpreadSheetID, > } > > resp, err = gs.Srv.Spreadsheets.Sheets.CopyTo(sourceSheetID, sourceSheetGID, requestBody).Context(gs.Ctx).Do() > if newName != "" && err == nil { > _, err2 := gs.SheetGIDRename(resp.SheetId, newName) > if err2 != nil { > err = err2 > } > } > > return resp, err > } > > ``` ### Go : Socket實作: > ##### ==Server端:== > ```go= > package main > import ( > "fmt" > "log" > "net" > "os" > "time" > ) > > const buffSize = 1024 > func main() { > > //建立socket,監聽埠 > listenPort := ":51680" > netListen, err := net.Listen("tcp", listenPort) > if err != nil { > log.Printf("listen tcp '%v' fail > '%v'\n", listenPort, err) > os.Exit(1) > } > defer netListen.Close() > > log.Println("Waiting for clients") > var clientList = make(map[*net.Conn]struct{}) > go func() { //監聽採用無限制迴圈 > for { > conn, err := netListen.Accept() > if err != nil { > continue > } > > clientIP := conn.RemoteAddr().String() > log.Println(clientIP, " tcp connect success") > buffer := make([]byte, buffSize) //超過緩存範圍訊息將被斷行打印 > go func() { //每個客戶端開goroutine > for { > clientList[&conn] = struct{}{} //struct不占記憶體空間 > endChr, err := conn.Read(buffer) //阻塞式,當無client連接時會進行等待 > if err != nil { > log.Println(clientIP, " connection error: ", err) > delete(clientList, &conn) > break > } > > switch string(buffer[:endChr]) { > case "bye": //關閉客戶端 > conn.Write([]byte("Bye bye!")) > delete(clientList, &conn) //移除廣播清單 > return //關閉goroutine > default: //接收客戶端訊息 > log.Println(clientIP, "receive data string:") > } > } > return > }() > > } > }() > > var sendMsg string > for { //廣播給所有清單內的客戶端 > fmt.Scanln(&sendMsg) > for client, _ := range clientList { > conn := *client > _, err := conn.Write([]byte(sendMsg)) > if err != nil { > log.Println("廣播 fail : ", conn.RemoteAddr()) > continue //監聽程序會負責移除斷線或異常的客戶端 > } > } > log.Printf("廣播 : %s", sendMsg) > time.Sleep(1 * time.Second) > } > } > ``` > > ##### ==Client端:== > ```go= > package main > import ( > "fmt" > "log" > "net" > "time" > ) > > const buffSize = 1024 > > func main(){ > > socketAddr := "localhost:51680" > var err error > var socketServer *net.Conn //socket server公用控制器 > go func(addr string) { > var conn net.Conn > buff := make([]byte, buffSize) > for { > if socketServer == nil { > conn, err = net.Dial("tcp", addr) //與server進行連線 > if err != nil { > log.Printf("Reconnect error:%v\n", err) > socketServer = nil > time.Sleep(1 * time.Second) > continue > } else { > socketServer = &conn > defer conn.Close() > log.Printf("Suecess to connect socket > '%v'\n", addr) > } > } > > endChr, err := conn.Read(buff) //接收server訊息 > if err != nil { > log.Println("Receive fail!") > socketServer = nil > continue > } > log.Printf("Received from server : %s", buff[:endChr]) > } > }(socketAddr) > > var sendMsg string > for { > fmt.Scanln(&sendMsg) > conn := *socketServer //指向公用socket server > _,err :=conn.Write([]byte(sendMsg)) > if err !=nil{ > log.Println("Sent fail !") > socketServer = nil > continue //若發送失敗接收者負責重連 > } > log.Printf("Send: %s", sendMsg) > time.Sleep(1 * time.Second) > } > } > > ``` ### Go : socket.io實作: > ##### ==Server端:== > ```go= > package main > > import ( > "fmt" > "log" > "net/http" > "time" > > socketio "github.com/googollee/go-socket.io" > ) > > const broadcastKey = "broadcast" > const clientKey = "client message" > > func main() { > server := socketio.NewServer(nil) > > var Clients = make(map[string]socketio.Conn) //連線中的Client清單 > server.OnConnect("/", func(conn socketio.Conn) error { > conn.SetContext("") > log.Printf("connected: %v (url:'%v' LocalAddr:'%v' RemoteAddr:'%v' RemoteHeader:'%v')", conn.ID(),conn.URL(),conn.LocalAddr(),conn.RemoteAddr(),conn.RemoteHeader()) > Clients[conn.ID()] = conn > return nil > }) > > server.OnDisconnect("/", func(conn socketio.Conn, reason string) { > delete(Clients, conn.ID()) //清除廣播清單 > log.Printf("%v closed : %v\n", conn.ID(), reason) > }) > > server.OnError("/", func(conn socketio.Conn, err error) { > log.Println("meet error:", err) //錯誤捕捉 > }) > > server.OnEvent("/", clientKey, func(conn socketio.Conn, msg string) { > if msg != "bye" { //當客戶端發送bye則將客戶端關閉 > log.Printf("get from %v : %v \n", conn.ID(), msg) > conn.Emit(clientKey, fmt.Sprintf("We have received your message : '%v'", msg)) //回傳給客戶端 > } else { > conn.Emit("bye !") > log.Printf("force close client %v", conn.ID()) > conn.Close() > } > }) > > go func(server *socketio.Server) { > for { > for index, value := range Clients { > value.Emit(broadcastKey, "廣播") > log.Printf("對%v 發出了廣播\n", index) > } > time.Sleep(5 * time.Second) > } > }(server) > > go server.Serve() > defer server.Close() > > http.Handle("/socket.io/", server) > http.Handle("/", http.FileServer(http.Dir("./asset"))) > log.Println("Serving at localhost:51680...") > log.Fatal(http.ListenAndServe(":51680", nil)) > } > ``` > ##### ==Client端:== > ```go= > package main > > import ( > "fmt" > "log" > "time" > > socketIoClient "github.com/mofadeyunduo/go-socket.io-client" > ) > > const broadcastKey = "broadcast" > const clientKey = "client message" > > func main() { > > // options 為選用功能 > // options := &socketIoClient.Options{ > // Transport: "websocket", > // Query: make(map[string]string), > // } > // options.Query["user"] = "user" > // options.Query["pwd"] = "pass" > > var socketClient *socketIoClient.Client //socket server 連線控制器 > socketHost := "http://localhost:51680" > socketOptions := &socketIoClient.Options{} > go func(client *socketIoClient.Client, uri string, opts *socketIoClient.Options) { //由於socket斷線後不會有任何提示,所以需要自行檢測與重連 > for { > if client == nil || client.Emit("ping", "") != nil { > var err error > client, err = socketIoClient.NewClient(uri, socketOptions) //與socket server 連線 > if err != nil { > client = nil //清空連線 > log.Printf("Reconnect error:%v\n", err) > } else { > socketClient = client > log.Printf("suecess to connect socket.io > '%v'\n", socketHost) > > client.On(broadcastKey, func(msg string) { //開啟監聽Server端廣播 > log.Printf("got broadcast: '%v'\n", msg) > }) > > client.On(clientKey, func(msg string) { //開啟監聽Server端傳訊 > log.Printf("got message: '%v'\n", msg) > }) > } > } > > time.Sleep(1 * time.Second) //每秒重新連線,並重新監聽 > } > }(socketClient, socketHost, socketOptions) > > var sendMsg string > for { > fmt.Scanln(&sendMsg) > if socketClient.Emit(clientKey, sendMsg) == nil { > log.Printf("sent : '%v'", sendMsg) > } else { > log.Println("sent fail !") > } > time.Sleep(1 * time.Second) //每秒重新連線,並重新監聽 > } > } > >