owned this note
owned this note
Published
Linked with GitHub
###### 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) //每秒重新連線,並重新監聽
> }
> }
>
>