Go语法知识笔记

前言

记录一些Go的语法糖,当然也可能会跳过一些简单的语法~

一、结构分析

package main

import (
	"fmt"
)

func Hello(str string) {
    
    
	fmt.Println("Hello " + str)
}

func main() {
    
    
	Hello("World!")
}

这是一个简单的Go代码,我们可以看到

  • 第一行package main代表这个文件属于main包的一部分,main包也就是程序的入口包。第一行包主代表这个文件属于主包的一部分,主包也就是程序的入口包。
  • 第三行import的操作表示的是引入一些标准库的包,可以简单的和C语言的#include预处理对应起来
  • 第七行这里就是Go的一个函数的结构了,我们这里接受一个string类型的str变量,然后将其加上Hello 前缀打印输出
  • 第十一行这里就是主函数了,类比C语言的main,即程序的启动入口

二、变量&常量

变量的基本类型:

①:bool类型
记录真或者假

②:数值类型

  • int8int16int32int64int
  • uint8uint16uint32uint64uint
  • float32, float64
  • complex64, complex128
  • byte
  • rune

记录数值

我们发现有一些变量后面跟了个数字,这其实就是表示这个变量的占用的位的大小,例如int8就表示占用8个bit,能表示的数字个数就是 2 8 2^8 28 考虑到 0 0 0 和负数的情况,那么这个int8表示的范围就是: − 2 7 -2^7 27 ~ 2 7 − 1 2^7-1 271 其他的一次类推了,其中前缀加了u的表示无符号型,即没有负数,直接从0 2 x − 1 2^x-1 2x1 即可

③:string类型

用于记录字符串类型的值

变量的定义

①:第一种通过var关键字来定义

扫描二维码关注公众号,回复: 15559245 查看本文章
var a = "hello"
var b = 3
var c float64 = 3.14

注意这里如果不给变量初始化的话,则需要带上这个变量的类型:

var a string
var b int
var c float64

②:直接定义

a := "hello"
b := 3
c := 3.14

当然变量定义的时候可以使用强转

常量的定义

常量的定义其实就是将变量的var变成const关键字即可

const str = "Hello World!"

三、分支&循环

if-else

package main

import (
	"fmt"
)

func main() {
    
    
	var a int
	fmt.Scanf("%d", &a)
	if a < 10 {
    
    
		fmt.Println("step1")
	} else if a < 20 {
    
    
		fmt.Println("step2")
	} else {
    
    
		fmt.Println("step3")
	}
}

这里我们发现和C/C++的区别在于判断条件没有括号包裹了,如果非要加括号,Go也是能运行的

switch case

package main

import (
	"fmt"
)

func main() {
    
    
	var a int
	fmt.Scanf("%d", &a)
	switch a {
    
    
	case 1:
		fmt.Println("This is 1")
	case 2:
		fmt.Println("This is 2")
	case 3:
		fmt.Println("This is 3")
	default:
		fmt.Println("This is error")
	}
}

  • c++里面,switch case如果不在目标case下加break的话会然后会继续往下跑完所有的case,在go语言 里面的话是不需要加break的。
  • 相比C或者C++go语言里面的switch功能更强大。可以使用任意的变量类型,甚至可以用来取代任意的if else语句。你可以在switch后面不加任何的变量,然后在case里面写条件分支。这样代码相比你用多个if else代码逻辑会更为清晰

for循环

Go只有一个for循环,不过也已经够了

package main

import (
	"fmt"
)

func main() {
    
    
	var a int
	fmt.Scanf("%d", &a)
	for i := 1; i < a; i++ {
    
    
		fmt.Println("index = ", i)
	}
}

while类型的写法:

package main

import (
	"fmt"
)

func main() {
    
    
	var a int
	fmt.Scanf("%d", &a)
	var i int = 1
	for i <= a {
    
    
		fmt.Println("index = ", i)
		i++
	}
}

for-range

for key, value := range oldMap {
    
    
    newMap[key] = value
}

注意这里的key有的时候不会用上,所以可以用_下划线代替,这样就不会报没使用这个变量的错了

四、数组

数组的定义只需要将申请的空间放在变量名和类型中间即可

eg:

//声明数组
var a[10]int
//声明并初始化操作
var d = [3]int{
    
    1,2,3}

二维数组类似:

var a[5][5]int
var d = [5][5]int{
    
    {
    
    1,2,3,4,5}}

数组长度我们可以通过len函数获取,例如

//声明数组
var a[10]int
//声明并初始化操作
var d = [3]int{
    
    1,2,3}
//输出长度
fmt.Println(len(d))

元素访问就是正常下标访问即可,例如:a[i][j]

五、函数

标准格式:

func function_name( [parameter list] ) [return_types] {
    
    
   函数体
}

结构记录:

// 传入两个 int 类型的值,然后返回其和,且也是int类型
func add(a int, b int) int {
    
    
	return a + b
}

// 传入一个stirng参数,然后没有返回值
func printt(str string) {
    
    
	fmt.Println("Hello", str)
}

Go函数可以返回多个值,一般来说,第一个是函数的返回值,第二个是错误信息。

func exists(m map[string]string, k string) (v string, ok bool) {
    
    
    v, ok = m[k]
    return v, ok
}

返回错误的情况:

func (c *Client) Do(req *Request) (*Response, error) {
    
    
	return c.do(req)
}

我们调用这个函数的时候就需要给出两个变量用于接受这个返回值:

resp, err := client.Do(req)

六、切片

切片(slice)与数组的区别是切片的长度不是固定的而是可变的,比数组的用途更加广泛。

切片声明需要通过 make 来进行,需要指定切片的类型以及长度,例如下面为创建一个长度为 3 3 3string类型的切片。

// 声明一个空切片
s1 := make([]string, 3)
// 声明并初始化
s2 :=[] int {
    
    1,2,3,4,5,6} 

切片除了长度还有一个容量(capacity)的概念,可以在声明切片的时候同时指定长度和容量。

s := make([]string, 3, 10)

通过s1 := s[startIndex:endIndex] 可以创造新切片s1 ,且s可以为数组,其余操作貌似和python的切片类似,不过go的切片更像一个变长数组或者说链表,go支持append函数用于向切片中追加值,支持copy函数用于复制切片

package main

import "fmt"

func main() {
    
    
   var numbers []int
   printSlice(numbers)

   /* 允许追加空切片 */
   numbers = append(numbers, 0)
   printSlice(numbers)

   /* 向切片添加一个元素 */
   numbers = append(numbers, 1)
   printSlice(numbers)

   /* 同时添加多个元素 */
   numbers = append(numbers, 2,3,4)
   printSlice(numbers)

   /* 创建切片 numbers1 是之前切片的两倍容量*/
   numbers1 := make([]int, len(numbers), (cap(numbers))*2)

   /* 拷贝 numbers 的内容到 numbers1 */
   copy(numbers1,numbers)
   printSlice(numbers1)  
}

func printSlice(x []int){
    
    
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

七、 指针

貌似和C/C++中的指针的操作是一样的,通过*访问,然后&是取地址符

不过Go中的指针不支持指针运算,例如下面的代码就会报错:

main.go:6: invalid operation: p++ (non-numeric type *[3]int)

package main
 
func main() {
    
      
    b := [...]int{
    
    109, 110, 111}
    p := &b
    p++
}

new创建指针

这里mark一下格式:

ptr := new(int) ,其中的int可以替换成其他数据类型

其他

  • 关于指针的解引用是和C/C++类似的

  • 想函数中传入指针参数的时候,我们在函数的定义时,需要使用*来表示接受指针的

感觉Go有个弱智的语法,先看代码:

package main

import (
	"fmt"
)

func find_max(a []int) int {
    
    
	l := len(a)
	ans := a[0]
	for i := 0; i < l; i++ {
    
    
		if a[i] > ans {
    
    
			ans = a[i]
		}
	}
	return ans
}

func main() {
    
    
	var d = [3]int{
    
    1, 2, 3}
	fmt.Println(find_max(d))
}

这也是C/C++正常的数组传参,不过这里会报错:

cannot use d (variable of type [3]int) as []int value in argument to find_max

意思大概就是a []int是一个切片,而传入的d是一个数组,所以会报错,我们需要将第 20 20 20 行的数据改为切片,即:
fmt.Println(find_max(d[:]))

八、结构体

结构体声明语法:

type StructName struct{
    
    
    FieldName type
}

不同成员之间直接换行写就行,不用,隔开,如下:

type StructName struct{
    
    
    a int
    b float32
    c string
    d bool
}

定义结构体变量:

var t1 StructName

t2 := StructName{
    
    
	a: 1,
	b: 1.1,
	c: "hello",
	d: true,
}

t3 := StructName{
    
    1, 1.1, "hello", true}

成员函数

成员函数写在结构体外面,格式如下:

func (t StructName) func_name(str string) int {
    
    
	
	return 1
}

这里我们将当前的这个结构体传入,当然也可以使用结构体指针,这样就能对当前的这个结构体变量的进行一些修改操作

json转结构体

JSON转Golang Struct

https://oktools.net/json2go

我们处理JSON数据的时候,需要将json构造成一个或者多个结构体的嵌套,那么假设我们有如下的JSON数据:

{
    
    
  "rc": 0,
  "wiki": {
    
    
    "known_in_laguages": 63,
    "description": {
    
    
      "source": "tangible and intangible thing, except labor tied services, that satisfies human wants and provides utility",
      "target": null
    },
    "id": "Q28877",
    "item": {
    
    
      "source": "good",
      "target": "商品"
    },
    "image_url": "http://www.caiyunapp.com/imgs/link_default_img.png",
    "is_subject": "true",
    "sitelink": "https://www.caiyunapp.com/read_mode/?id=6354777915466339550246c5"
  },
  "dictionary": {
    
    
    "prons": {
    
    
      "en-us": "[gʊd]",
      "en": "[gud]"
    },
    "explanations": [
      "a.好的;善良的;快乐的;真正的;宽大的;有益的;老练的;幸福的;忠实的;优秀的;完整的;彻底的;丰富的",
      "n.利益;好处;善良;好人",
      "ad.=well"
    ],
    "synonym": [
      "excellent",
      "fine",
      "nice",
      "splendid",
      "proper"
    ],
    "antonym": [
      "bad",
      "wrong",
      "evil",
      "harmful",
      "poor"
    ],
    "wqx_example": [
      [
        "to the good",
        "有利,有好处"
      ],
      [
        "good, bad and indifferent",
        "好的,坏的和一般的"
      ],
      [
        "good innings",
        "长寿"
      ],
      [
        "good and ...",
        "很,颇;完全,彻底"
      ],
      [
        "do somebody's heart good",
        "对某人的心脏有益,使某人感到愉快"
      ],
      [
        "do somebody good",
        "对某人有益"
      ],
      [
        "be good for",
        "对…有效,适合,胜任"
      ],
      [
        "be good at",
        "在…方面(学得,做得)好;善于"
      ],
      [
        "as good as one's word",
        "信守诺言,值得信赖"
      ],
      [
        "as good as",
        "实际上,几乎等于"
      ],
      [
        "all well and good",
        "也好,还好,很不错"
      ],
      [
        "a good",
        "相当,足足"
      ],
      [
        "He is good at figures . ",
        "他善于计算。"
      ]
    ],
    "entry": "good",
    "type": "word",
    "related": [],
    "source": "wenquxing"
  }
}

让我们自己写结构体,也许能写但是回很麻烦,这个时候就需要用到上面的工具了JSON转Go的结构体

转换出来:

type AutoGenerated struct {
    
    
	Rc int `json:"rc"`
	Wiki struct {
    
    
		KnownInLaguages int `json:"known_in_laguages"`
		Description struct {
    
    
			Source string `json:"source"`
			Target interface{
    
    } `json:"target"`
		} `json:"description"`
		ID string `json:"id"`
		Item struct {
    
    
			Source string `json:"source"`
			Target string `json:"target"`
		} `json:"item"`
		ImageURL string `json:"image_url"`
		IsSubject string `json:"is_subject"`
		Sitelink string `json:"sitelink"`
	} `json:"wiki"`
	Dictionary struct {
    
    
		Prons struct {
    
    
			EnUs string `json:"en-us"`
			En string `json:"en"`
		} `json:"prons"`
		Explanations []string `json:"explanations"`
		Synonym []string `json:"synonym"`
		Antonym []string `json:"antonym"`
		WqxExample [][]string `json:"wqx_example"`
		Entry string `json:"entry"`
		Type string `json:"type"`
		Related []interface{
    
    } `json:"related"`
		Source string `json:"source"`
	} `json:"dictionary"`
}

九、错误处理

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

error类型是一个接口类型,这是它的定义:

type error interface {
    
    
    Error() string
}

如果是函数内部抛出异常,那么对于当前函数就应该多一个errors的接收变量,例如下面的代码示例:

type user struct {
    
    
	name string
	password string
}

func findUser(users []user, name string) (v *user, err error) {
    
    
    for _, u := range users {
    
    
        if u.name == name {
    
    
            return &u, nil
        }
    }
    return nil, errors.New("not found")
}

当我们需要调用该函数的时候,需要如此调用:

us,err = findUser([]user{
    
    {
    
    "xie","1234abcd"}},"xie")

十、标准库

Go内置了非常丰富的标准库工具,常用的标准库包括字符串操作、字符串格式化、json处理、时间处理等。

字符串操作


a := "hello"
// 是否包含
fmt.Println(strings.Contains(a, "ll"))                // true
// 字符统计
fmt.Println(strings.Count(a, "l"))                    // 2
// 判断字符串开头
fmt.Println(strings.HasPrefix(a, "he"))               // true
// 判断字符串结尾
fmt.Println(strings.HasSuffix(a, "llo"))              // true
// 查找字符串
fmt.Println(strings.Index(a, "ll"))                   // 2
// 字符串拼接
fmt.Println(strings.Join([]string{
    
    "he", "llo"}, "-")) // he-llo
// 复制字符串指定次数
fmt.Println(strings.Repeat(a, 2))                     // hellohello
// 字符串替换
fmt.Println(strings.Replace(a, "e", "E", -1))         // hEllo
// 字符串分割
fmt.Println(strings.Split("a-b-c", "-"))              // [a b c]
// 转为小写
fmt.Println(strings.ToLower(a))                       // hello
// 转为大写
fmt.Println(strings.ToUpper(a))                       // HELLO
// 字符串长度
fmt.Println(len(a))                                   // 5

字符串格式化

Println 最为常用作用为打印并换行,Printf 可以按指定格式打印字符串。

+v可以打印字段和值详细信息。

#v可以打印出整个结构体的构造以及详细信息。

s := "hello"
n := 123
p := point{
    
    1, 2}
fmt.Println(s, n) // hello 123
fmt.Println(p)    // {1 2}

fmt.Printf("s=%v\n", s)  // s=hello
fmt.Printf("n=%v\n", n)  // n=123
fmt.Printf("p=%v\n", p)  // p={1 2}
fmt.Printf("p=%+v\n", p) // p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // p=main.point{x:1, y:2}

f := 3.141592653
fmt.Println(f)          // 3.141592653
fmt.Printf("%.2f\n", f) // 3.14

json 处理

Go在处理 json 时十分简单,只需要将结构体中的字段第一个字母变成大写就能用内置的JSON工具进行处理。

Go中一些特殊的类型,比如Channelcomplexfunction是不能被解析成JSON的。

GoJSON对象只支持string作为key,对于map,那么必须是map[string]T这种类型,T 可以是 Go 语言中任意的类型。

Gojson处理是通过MarshalUnmarshal方法来进行处理的。

Marshal 用于自定义编码json的方法,也就是将变量、对象转成json,转换之后需要用string方法强转一下,否则会打印出的是 16 16 16 进制字符串。

Unmarshal用于自定义解码json方法,也就是将json转为对象。

type userInfo struct {
    
    
    Name  string
    Age   int `json:"age"` // 自定义json输出的字段
    Hobby []string
}

func main() {
    
    
    a := userInfo{
    
    Name: "wang", Age: 18, Hobby: []string{
    
    "Golang", "TypeScript"}}
    buf, err := json.Marshal(a)
    if err != nil {
    
    
        panic(err)
    }
    fmt.Println(buf)         // [123 34 78 97...]
    fmt.Println(string(buf)) // {"Name":"wang","age":18,"Hobby":["Golang","TypeScript"]}

    buf, err = json.MarshalIndent(a, "", "\t")
    if err != nil {
    
    
        panic(err)
    }
    fmt.Println(string(buf))

    var b userInfo
    err = json.Unmarshal(buf, &b)
    if err != nil {
    
    
        panic(err)
    }
    fmt.Printf("%#v\n", b) 
// main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "TypeScript"}}

时间处理

Go提供了很多常用的时间处理函数,例如 Now、解析字符串、转字符串、获取时间戳等。在操作时间相关的方法时,需要导入time包。

// 获取当前时间
now := time.Now()
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933
t := time.Date(2022, 3, 27, 1, 25, 36, 0, time.UTC)
t2 := time.Date(2022, 3, 27, 2, 30, 36, 0, time.UTC)
fmt.Println(t)                                                  // 2022-03-27 01:25:36 +0000 UTC
// 获取时间的年月日
fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) // 2022 March 27 1 25
// 时间转字符串
fmt.Println(t.Format("2006-01-02 15:04:05"))                    // 2022-03-27 01:25:36
// 获取时间差
diff := t2.Sub(t)
fmt.Println(diff)                           // 1h5m0s
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
// 解析字符串
t3, err := time.Parse("2006-01-02 15:04:05", "2022-03-27 01:25:36")
if err != nil {
    
    
        panic(err)
}
fmt.Println(t3 == t)    // true
// 获取时间戳
fmt.Println(now.Unix()) // 1648738080

十一、数据结构

Go语言内置了一个数据结构,即map

Map 是一种无序的键(key)值(value)对的集合,也被称为映射、字典。

Map是无序的,支持类似数组和切片的操作。

Map的声明如下:

m := make(map[string]int)

其中,string为键,int为值,对元素的访问也是按照下标进行访问,对于不存在的键值对,返回0,这里也有一个问题,假设一个键值对的key也是0,有的时候就可能存在误解,于是Go在键值对的查询结果时,返回了两个值,一个是结果,一个是,是否存在,如下:

r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false

删除键值对,可以使用delete函数

delete(m, "one")

另外有一个大佬用Go实现了STL

GoSTL是一个go语言数据结构和算法库,类似C++STL,但功能更强大。结合go语言的特点,大部分数据结构都实现了协程安全,可以在创建对象的时候通过配置参数指定是否开启。

gostl:https://github.com/liyue201/gostl

参考博客

猜你喜欢

转载自blog.csdn.net/m0_46201544/article/details/128727128