七叶笔记 » golang编程 » Golang的基本语法

Golang的基本语法

数据类型

  布尔型: var b bool = true。
 数字类型: 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。
 派生类型:
     包括:
         (a) 指针类型(Pointer)
         (b) 数组类型
         (c) 结构化类型(struct)
         (d) Channel 类型
         (e) 函数类型
         (f) 切片类型
         (g) 接口类型(interface)
         (h) Map 类型  

数组、切片、指针

  Go语言切片是对数组的抽象,Go数组的长度不可改变,在特定场景中这样的集合不适用,Go提供了一种灵活,功能强悍的内置类型切片(“动态数据”)
 //数组用法
 var hello [10] float32
 var hello = [5]float32{1.0,2.0,3.0,4.0,5.0}
 var hello = [...]float32{1.0,2.0} //根据元素个数设置数组大小
 hello[1] 为 = 2.0
 
 //切片用法
 声明:var hello []int 
 声明:var hello []int = make([]int, len) 
 或 hello := make([]int,len,cap)  

Map

普通 map

    

多协程 map

多协程 map:普通的 map 在多协程操作时, 是不支持并发写入的. go 贴心的给封装了支持并发写入的 map . 同时也提供了针对 map 的基本操作

  var m = sync.Map{}
 func main() {
     for i := 0; i < 100; i++ {
         go func() {
             m.Store("1", 1)
         }()
     }
     time.Sleep(time.Second * 2)
     // 遍历 map
     m.Range(func(key, value interface{}) bool {
         // 返回 false 结束遍历
         return true
     })
     // 读取变量, 若不存在则设置
     m.LoadOrStore("1", 3)
     // 删除 key
     m.Delete("1")
     // 读取变量
     load, _ := m.Load("1")
     fmt.Println(load)
 }  

内置函数make、new

  • new func new(Type) *Type
    用来分配内存,第一个参数是一个类型,返回值是一个指向新分配类型 零值的指针
  • make func make(t Type, size …IntergerType) Type
    用来为 slice,map或 chan类型分配内存和初始化一个对象(注意:只能用在这三种类型上)。跟 new 不同的是,make返回类型的引用而不是指针,而返回值也依赖于具体传入的类型。
  • new和其他语言中同名函数一样,new(t) 分配了 “零值填充的T类型的内存空间,并且返回其地址,即一个 *t 类型的值,它并不初始化内存,只是将其置零。 *t 指向的内容的值为零,注意不是指针为零。
  • make(t, args) 与 new(t) 的功能区别是,make 只能创建 slice、map、channel,并且返回一个初始化的(而不是置零),类型为 t 的值(而不是 *t

行分隔符与注释

  一行代表一个语句结束,并不是以分号结尾。如果想将多个语句写在同一行,则必须使用分号,但不鼓励这样做。
 // 单行注释
 /*
     多行注释
 */  

变量的声明和赋值

  var a int   //变量的声明
 a = 123     //变量的赋值
 //变量声明的同时赋值
 var b int = 321
 //上行的格式可省略变量类型,由系统推断
 var c = 321
 //变量声明和赋值的最简写法
 d := 456  

编程基础

  • 通过 import 关键字来导入其他非 main 包
  • 通过 const 关键字来进行常量的定义
  • 通过在函数体外部使用 var 关键字来进行全局变量的声明和赋值全局变量的声明可使用 var () 方式进行简写所有变量都可以使用类型推断全局变量声明不可以省略 var,但可以使用并行方式 var (
    // 使用常规方式
    aaa = “hello”
    // 使用并行方式及类习惯推断
    sss, bbb = 1, 2
    //ccc := 3 //不可以省略 var
    )

    //多个变量的声明
    var a,b,c,d int
    //多个变量的赋值
    a,b,c,d = 1,2,3,4
    //多个变量声明同时赋值
    var e,f,g,h int = 5,6,7,8
    //省略变量类型,由系统推断
    var i,j,k,l = 9,10,11,12
    //多个变量声明与赋值的最简写法
    i,m,n,o := 13,14,15,16

    const (
    text = “123”
    length = len(text)
    )

    局部变量不可以使用var()方式简写,只能使用并行方式
  • 通过type关键字来进行结构(struct)和接口(interface)的声明
  • 通过func关键字来进行函数的声明

大小写

Go语言中,使用大小写来决定该常量、变量、类型、接口、结构或函数 是否可以被外部包所调用;

数据类型间转换

  • 判断变量的数据类型
  package main
 import (
     "fmt"
     "reflect"
 )
 
 func main() {
     var x float64 = 3.4
     var zifu string = "zhangsan"
     
     fmt.Println("x 的数据类型是:",reflect.TypeOf(x))
     // fmt.Printf(“%T”)里最终调用的还是reflect.TypeOf();Printf不自动换行,\n 是为了换行
     fmt.Printf("x 的数据类型是: %T\n",x) 
     fmt.Println("zifu 的数据类型是:",reflect.TypeOf(zifu))
     fmt.Printf("zifu 的数据类型是: %T",zifu)
 }  
  • int 和 string //int -> string
    string := strconv.Itoa(int)
    //string -> int
    int, err := strconv.Atoi(string)

    //int64 -> string
    string := strconv.FormatInt(int64, 10)
    //string -> int64
    int64, err := strconv.Parselnt(string, 10, 64)

  • float 和 string //float32/float64 -> string
    string := strconv.FormatFloat(float32/float64, ‘E’, -1, 32/64)

    //string –> float32/float64
    float32/float64, err := ParseFloat(string, 32/64)

  • int 和 int64 //int -> int64
    string := strconv.Itoa(int)
    int64 := strconv.ParseInt(string, 10, 64)
  • interface 和 string、int、float64、[]btye、map //interface -> string;interface -> []byte
    var a interface{}
    var str5 string
    a = “3432232”
    str5 = a.(string)
    strbyte := a.([]byte)

    //interface -> int
    var m interface{}
    var m1 int
    m = 43
    m1 = m.(int)

    //interface -> float64
    var ff interface{}
    var ff1 float64
    ff = 432.54
    ff1 = ff.(float64)

    //interface -> map
    dd := `
    {
    “result_code”: “200”,
    “biz_response”: {
    “result_code”: “PAY_SUCCESS”,
    “data”: {
    “sn”: “7895059720469388”,
    “client_sn”: “202104201618897358976887”,
    }
    }
    }
    `
    var dat map[string]interface{}
    json.Unmarshal([]byte(dd), &dat)
    biz_response := dat[“biz_response”].(map[string]interface{})
    fmt.Println(“biz_response,result_code=”, biz_response[“result_code”])
    data := biz_response[“data”].(map[string]interface{})
    fmt.Println(“data,sn=”, data[“sn”])

  • string 和 []byte string([]byte)
    []byte(string)
    string(byte[:])
  • interface 和 struct —————————————- interface{} 转为 struct —————————————-
    type user struct {
    Id int `json:”id”`
    Name string `json:”name”`
    }

    newUser:=user{
    Id: 1,
    Name: “1212”,
    }

    var newInterface1 interface{}
    //第一种使用interface 强制转换
    newInterface1 = newUser
    fmt.Printf(“使用interface: %v”,newInterface1.(user))

    //第二种使用json
    var newInterface2 interface{}
    newInterface2 = newUser
    resByre, resByteErr := json.Marshal(newInterface2)
    if resByteErr != nil {
    fmt.Printf(“%v”,resByteErr)
    return
    }
    var newData user
    jsonRes := json.Unmarshal(resByre, &newData)
    if jsonRes != nil {
    fmt.Printf(“%v”,jsonRes)
    return
    }
    fmt.Printf(“使用 json: %v”,newData)

Golang 运算符

  • 算术运算符:+、-、*、/、%、++、–
  • 关系运算符:==、!=、>、<、>=、<=
  • 逻辑运算符:&&、||、!
  • 位运算符:对整数在内存中的二进制位进行操作,按位与&、按位或|、按位异或^、左移<<、右移>> var a uint = 60 // 60 = 0011 1100
    var b uint = 13 // 13 = 0000 1101
    var c uint = 0
    c = a & b // 12 = 0000 1100
    c = a | b // 61 = 0011 1101
    c = a ^ b // 49 = 0011 0001 只要位不同结果为1,不然结果为0
    c = a << 2 // 240 = 1111 0000
    c = a >> 2 // 15 = 0000 1111
  • 赋值运算符:= += -= *= /= %= <<= >>= &= ^= |=
  • 其他运算符:& 返回变量存储地址、* 指针变量

Golang 条件语句

  • if 条件表达式没有括号支持一个初始化表达式(可以是并行式)左大括号必须和条件语句或else在同一行支持单行模式初始化语句中的变量为 block 级别,同时隐藏外部同名变量
  func main(){
     a := true
     if a,b,c := 1,2,3; a+b+c > 6{
         //...
     }else{
         //...
     }
 }  
  1. 支持多条件匹配。
     switch {
         case 1,2,3,4:
         default:
     }
 2. 不同的 case 之间不使用 break 分隔,默认只执行一个 case。
 3. 如果想要执行多个 case,需要使用 fallthrough 关键字,也可以用 break 终止。
     switch {
         case 1:
         ...
         if ... {
             break
         }
         
         fallthrough //此时switch(1)会执行case 1和case 2,但是如果满足if条件,则只执行case 1
         case 2:
         case 3:
     }  
  select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句,每个 case 必须是一个通信操作,要么是发送要么是接收。
 select 随机执行一个可运行的 case,如果没有 case 可执行,它将阻塞,直到有 case 可运行。
 
 package main
 import "fmt"
 func main() {
    var c1, c2, c3 chan int
    var i1, i2 int
    select {
       case i1 = <-c1:
          fmt.Printf("received ", i1, " from c1\n")
       case c2 <- i2:
          fmt.Printf("sent ", i2, " to c2\n")
       case i3, ok := (<-c3):  // same as: i3, ok := <-c3
          if ok {
             fmt.Printf("received ", i3, " from c3\n")
          } else {
             fmt.Printf("c3 is closed\n")
          }
       default:
          fmt.Printf("no communication\n")
    }    
 }
 // no communication
 1. 每个 case 都必须是一个通信;
 2. 所有 channel 表达式都会被求值
 3. 所有被发送的表达式都会被求值
 4. 如果任意某个通信可以进行,它就执行;其他被忽略;
 5. 如果多个 case 都可以运行,select会随机公平地选出一个执行。其他不会执行。
 否则:
     1. 如果有 default 子句,则执行该语句。
     2. 如果没有 default 子句,select 将阻塞,直到某个通讯可以运行;Go 不会重新对 channel 或值进行求值。  

golang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的操作。在执行 select 语句的时候,运行时系统会自上而下地判断每个 case 中的发送或接收操作是否可以被立即执行。

Go 循环语句

for(Go 只有for一个循环语句关键字)

  • 和 C 语言的 for 一样: for init; condition; post {}
  • 和 C 语言的 while 一样: for condition {}
  • 和 C 的 for(;;) 一样: for {}
  • for 循环的 range 格式可以对 slice、map、数组、字符串 等进行迭代循环 for key, value := range oldMap{
    newMap[key] = value
    }
  • package main
    import “fmt”
    func main(){
    var b int = 15
    var a int
    numbers := [6]int{1,2,3,5}
    for a := 0; a<10; a++{
    fmt.Printf(“a=%d\n”, a)
    }
    for a < b{
    a++
    fmt.Printf(“a=%d\n”,a)
    }
    for i,x := range number{
    fmt.Printf(“第%d位 x 的值=%d\n”, i,x)
    }
    }
  • break,经常用于中断当前 for 循环或跳出 switch 语句
  • continue,跳过当前循环的剩余语句,然后继续进行下一轮循环
  • goto,将控制转移到被标记的语句
  • 三个语句都可以配合标签使用,标签名区分大小写,若不使用会造成编译错误,break和continue配合标签可用于多层循环的跳出,goto是调整执行位置,与其它2个语句配合标签的结构并不相同。
  • func main(){
    LABEL:
    for {
    for i := 0; i < 10; i++ {
    if i > 2 {
    break LABEL
    } else {
    fmt.Println(i)
    }
    }
    }
    }

    func main(){
    LABEL:
    for i := 0; i < 10; i++ {
    for {
    fmt.Println(i)
    continue LABEL
    }
    }
    }

语言范围(Range)

Go语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回key-value对的key值。

  nums := []int{1,2,3}
 for _, num := range nums{
     //
 }
 //range也可以用在map的键值对上。
 kvs := map[string]string{"a": "apple", "b": "banana"}
 for k, v := range kvs {
     fmt.Printf("%s -> %s\n", k, v)
 }
 //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
 for i, c := range "go" {
     fmt.Println(i, c)
 }  

并发、通道

  • 并发 goroutine 由官方实现的超级 “线程池” ,每个实例 4-5KB 的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。goroutine 的本质是协程,是实现并行计算的核心。goroutine使用方式非常的简单,只需使用go关键字即可启动一个协程,并且它是处于异步方式运行。
  • 通道(channel)是用来 传递数据的一个数据结构 ,通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
  • 不带缓冲区的通道 ch := make(chan int) 通过make创建,close关闭。 ch <- v //把 v 发送到通道 ch v:= <-ch //从ch接收数据,并把值赋给 v 。默认情况下,通道是不带缓冲区的,发送端发送完数据后,就会阻塞,直到有接收端接收了数据。
  • 带通道缓冲区 通道可以设置缓冲区 ch := make(chan int, 100) ,带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态。如果缓冲区已满,则需要等待,直到某个接收方获取到一个值,期间处于阻塞状态。
  • Go 通过 range 关键字来实现遍历读取通道的数据,类似于与数组或切片:

JSON

  package main
 
 import (
     "encoding/json"
     "fmt"
     "os"
 )
 
 type ConfigStruct struct {
     Host              string   `json:"host"`
     Port              int      `json:"port"`
     AnalyticsFile     string   `json:"analytics_file"`
     StaticFileVersion int      `json:"static_file_version"`
     StaticDir         string   `json:"static_dir"`
     TemplatesDir      string   `json:"templates_dir"`
     SerTcpSocketHost  string   `json:"serTcpSocketHost"`
     SerTcpSocketPort  int      `json:"serTcpSocketPort"`
     Fruits            []string `json:"fruits"`
 }
 
 type Other struct {
     SerTcpSocketHost string   `json:"serTcpSocketHost"`
     SerTcpSocketPort int      `json:"serTcpSocketPort"`
     Fruits           []string `json:"fruits"`
 }
 
 type FoodInfo struct {
     Name      string
     Price     int
     OtherName string
 }
 
 type FoodInfoSlice struct {
     FoodInfos []FoodInfo
 }
 
 /*
     json str 转 map
     json str 转 struct
     json str 转 struct(部份字段)
     json 到 []string
 
     struct 到 json str
     map1 到 json str
     map2 到 json str
     array 到 json str
 */ 
 /*
     组建如下格式
     [
         {"Name":"包子", "Price":550, "OtherName":"包子" },
         {"Name":"馒头", "Price":400, "OtherName":"馒头" }
     ]
 */ 
 func main() {
     jsonStr := `{
         "host": "#34;,
         "port": 9090,
         "analytics_file": "",
         "static_file_version": 1,
         "static_dir": "E:/Project/goTest/src/",
         "templates_dir": "E:/Project/goTest/src/templates/",
         "serTcpSocketHost": ":12340",
         "serTcpSocketPort": 12340,
         "fruits": ["apple", "peach"]
     }`
 
     //json str 转map
     var dat map[string]interface{}
     if err := json.Unmarshal([]byte(jsonStr), &dat); err == nil {
         fmt.Println("==============json str 转 map=======================")
         //      fmt.Println(dat)
         fmt.Println(dat["host"])
     }
 
     //json str 转struct
     var config ConfigStruct
     if err := json.Unmarshal([]byte(jsonStr), &config); err == nil {
         fmt.Println("==============json str 转 struct====================")
         //      fmt.Println(config)
         fmt.Println(config.Host)
     }
 
     //json str 转struct(部份字段)
     var part Other
     if err := json.Unmarshal([]byte(jsonStr), ∂); err == nil {
         fmt.Println("==============json str 转 struct====================")
         fmt.Println(part)
         fmt.Println(part.SerTcpSocketPort)
     }
 
     //struct 到json str
     if b, err := json.Marshal(config); err == nil {
         fmt.Println("==============struct 到 json str===================")
         fmt.Println(string(b))
     }
 
     //map1 到json str
     fmt.Println("==================map1 到 json str==========================================??????????????????????")
     enc := json.NewEncoder(os.Stdout)
     enc.Encode(dat)
 
     //map2 到json str
     fmt.Println("==================map2 到 json str==========================================")
     map2 := make(map[string]interface{})
     map2["cmd"] = "begin study"
     map2["msg"] = "hello map"
     if json2, err := json.Marshal(map2); err == nil {
         fmt.Println(string(json2))
     }
 
     //array 到 json str
     arr := []string{"hello", "apple", "python", "golang", "base", "peach", "pear"}
     lang, err := json.Marshal(arr)
     if err == nil {
         fmt.Println("================array 到 json str===================")
         fmt.Println(string(lang))
     }
 
     //json 到 []string
     var wo []string
     if err := json.Unmarshal(lang, &wo); err == nil {
         fmt.Println("================json 到 []string===================")
         fmt.Println(wo)
     }
 
     //组建 arrayjson
     var arrjson FoodInfoSlice
     arrjson.FoodInfos = append(arrjson.FoodInfos, FoodInfo{Name: "包子", Price: 550, OtherName: "包子"})
     arrjson.FoodInfos = append(arrjson.FoodInfos, FoodInfo{Name: "馒头", Price: 400, OtherName: "馒头"})
     sarrayjson, _ := json.Marshal(arrjson)
     fmt.Println("================组建 arrjson 到 json str===================")
     fmt.Println(string(sarrayjson))
 }  

格式化字符串运算符

  %v    值的默认格式表示。当输出结构体时,扩展标志(%+v)会添加字段名
 %#v    值的Go语法表示
 %T    值的类型的Go语法表示
 %%    百分号
 %t    单词true或false
 %b    表示为二进制
 %c    该值对应的unicode码值
 %d    表示为十进制
 %o    表示为八进制
 %q    该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
 %x    表示为十六进制,使用a-f
 %X    表示为十六进制,使用A-F
 %U    表示为Unicode格式:U+1234,等价于"U+%04X"
 %b    无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat %e    科学计数法,如-1234.456e+78 %E    科学计数法,如-1234.456E+78 %f    有小数部分但无指数部分,如123.456 %F    等价于%f %g    根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
 %G    根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
 %s    直接输出字符串或者[]byte %q    该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
 %x    每个字节用两字符十六进制数表示(使用a-f)
 %X    每个字节用两字符十六进制数表示(使用A-F)
 %p    表示为十六进制,并加上前导的0x
 %f:    默认宽度,默认精度
 %9f    宽度9,默认精度
 %.2f   默认宽度,精度2 %9.2f  宽度9,精度2 %9.f   宽度9,精度0  

字符串拼接与格式化

参考

  1. s += str
 2. s = fmt.Sprintf("%s%s", s, str)
 3. var builder strings.Builder  builder.WriteString(str)   builder.String()
 4. buf := new(bytes.Buffer)   buf.WriteString(s)  buf.String()
 5. buf := make([]byte, 0)  buf = append(buf, str...)  string(buf)
 一般推荐使用 strings.Builder 来拼接字符串。  

文件路径操作

  path/filepath 包  

字符串操作

  strings  

相关文章