Go语言基础 | 第五届青训营

这是我参与「第五届青训营 」第 1 天

课程内容主要包括:
1.Go语言简介
2.开发环境搭建
3.语法基础
4.实战示例

1.Go语言简介

  • 具有高性能、高并发、语法简单的特点
  • 拥有丰富的标准库、完善的工具链
  • 能够快速编译、支持跨平台
  • Java一样不需要自己释放内存,拥有垃圾回收机制

2.开发环境搭建

  • 安装Go: 访问 go.dev/ 或者访问 studygolang.com/dl选择对应平台进行下载安装
  • 配置环境: 目前使用广泛的IDE是 VS CodeGolandVS Code 需要额外安装Go语言的插件,Goland 是收费软件,可以注册账号进行学生认证。

3.语法基础

Hello Word

每个Go程序都是由包构成的,程序从main包开始运行。Hello Word程序导入了fmt包用来进行格式化输出。fmt包中格式化输出字符串的函数还有PrintfSprintfFprintf等,读取格式化字符串的函数有ScanfSscanfFscanf等。

package main
import (
    "fmt"
)
func main() { 
fmt.Println("Hello World") 
}

变量

变量声明有两种方式,一种是var关键字声明,可以写在函数体外,声明并初始化基本语法如下:

package main
import "fmt"
var (
    i, j int = 1, 2
    k string = "yes"
)
func main() {
	var a, b, c = true, false, "this"
	fmt.Println(i, j, k, a, b, c)
}

另一种是通过简洁赋值语句:= 在类型明确的地方代替第一种声明方式,但不能在函数体外面使用,基本语法如下:

package main
import "fmt"
func main() {
	k := float32(3)
	fmt.Println(k)
}

另外,Go中不允许变量不被使用。
常量没有特定类型,将var改成const,不能用 := 语法声明。也能够根据上下文确定类型。

if else

Go 的 if 语句,表达式外无需小括号 ( ) ,而大括号 { } 则必须。if 语句可以在条件表达式前执行一个简单的语句。该语句声明的变量作用域仅在 if 之内。

package main
import (
	"fmt"
	"math"
)
func pow(x, n, lim float64) float64 {
	if v := math.Pow(x, n); v < lim {
		return v
	} else {
		fmt.Printf("%g >= %g\n", v, lim)
	}
	// 这里开始就不能使用 v 了
	return lim
}
func main() {
	fmt.Println(
		pow(3, 2, 10),
		pow(3, 3, 20),
	)
//在 main 的 fmt.Println`调用开始前,两次 pow 的调用执行并返回输出为
//27 >= 20
//9 20
}

循环

Go 中只有 for 关键字用于循环,与if 一样大括号 { } 必须。

package main
import "fmt"
func main() {
	sum := 0
	for i := 0; i < 10; i++ {
		sum += i
	}
        //初始条件和后置语句可选,去掉分号和while一样,什么都不写是死循环
        for ; sum < 1000; {
		sum += sum
	}
	fmt.Println(sum)
	fmt.Println(sum)
}

switch

Go 的 switch 语句类似于 C、C++、Java、JavaScript 和 PHP 中的,不过 Go 只运行选定的 case,而非之后所有的 case。 实际上,Go 自动提供了在这些语言中每个 case 后面所需的 break 语句。 除非以 fallthrough 语句结束,否则分支会自动终止。 Go 的另一点重要的不同在于 switch 的 case 无需为常量,且取值不必为整数。

package main
import (
    "fmt"
    "time"
)
func main() {
    a := 2
    switch a {
    case 1:
        fmt.Println("one")
    case 2:
        fmt.Println("two")
    case 3:
        fmt.Println("three")
    case 4, 5:
        fmt.Println("four, five")
    default:
        fmt.Println("other")
    }
    t := time.Now()
    switch {//在switch中不加判断条件,而是在case中去写条件分支,这样可以处理多个条件
    case t.Hour() < 12:
        fmt.Println("before 12'")
    default:
        fmt.Println("after 12'")
    }
}
}

数组

数组长度不能改变,切片弥补了这一缺陷,比数组更常用。

package main
import "fmt"
func main() {
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println(a[0], a[1])
	fmt.Println(a)

	primes := [6]int{2, 3, 5, 7, 11, 13}
	fmt.Println(primes)
}
//Hello World
//[Hello World]
//[2 3 5 7 11 13]

切片

类型 []T 表示一个元素类型为 T 的切片,例如:

arr := [5]int{1, 2, 3, 4, 5} 
slice := arr[1:4]

切片实际上存储了一个长度和容量以及指向底层数组的指针,容量不够会扩容返回新的切片。可以通过make([]type, len, cap)函数来创建
是一个左闭右开的区间。更改切片的元素会修改其底层数组中对应的元素。与它共享底层数组的切片都会更新。与python一样的切片操作,但不支持负数索引。

package main
import "fmt"
func main() {
	//使用make创建切片
	s := make([]string, 3)
	s[0] = "a"
	s[1] = "b"
	s[2] = "c"
	fmt.Println("get:", s[0])//a
	fmt.Println("len:", len(s))// 3

	//切片可以使用append追加元素
	s = append(s, "d")
	s = append(s, "e", "f") 
	fmt.Println(s)//[a b c d e f]

	//使用make的时候也可以直接指定长度进行创建
	c := make([]string, len(s))
	copy(c, s) 
	fmt.Println(c)//[a b c d e f]

       fmt.Println(s[2:5])//[c d e]左闭右开
	fmt.Println(s[:5])//[a b c d e] 取出5之前的元素,0 1 2 3 4
	fmt.Println(s[2:])//[c d e f] 取出第二个及以后的元素
        
	good := []string{"1", "2", "3", "4"}
	fmt.Println(good)//[1 2 3 4]
}

map

Go中的map是完全无序的,遍历是按随机顺序。是一个映射,存储键值对,可以用make函数创建。

package main

import "fmt"

func main() {
	m := make(map[string]int)//空map,键字符串,值整数
	m["one"] = 1             
	m["two"] = 2
	fmt.Println(m)           //map[one:1 two:2]
	fmt.Println(len(m))      //2
	fmt.Println(m["one"])    //1
	fmt.Println(m["unknow"]) //0

	r, ok := m["unknow"]     //使用索引运算符检索特定键的值
	fmt.Println(r, ok) //0 false

	delete(m, "one")         //使用delete函数从map中删除键值对

	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	println(m2, m3) 
}

range

对于数组、切片和字符串,它将返回每个元素的索引和值。对于map,它将返回每个键和值。对于通道,它将返回每个从通道中接收到的值。在遍历时如果不使用可以用 _ 来忽略这个变量。

package main
import "fmt"
func main() {
	nums := []int{2, 3, 4}
	sum := 0
	for i, num := range nums {
		sum += num
		if num == 2 {
			fmt.Println("index:", i, "num:", num)
                        //index: 0 num: 2
		}
	}
	fmt.Println(sum)//9

	m := map[string]string{"a": "A", "b": "B", "c": "C"}
	for k, v := range m {//返回每个键和值
		fmt.Println(k, v)//c C a A b B
	}
	for k := range m {
		fmt.Println("key:", k)//key: b  key: c  key: a
	}
}

函数

Go 中的函数可以有多个返回值,在实际业务中一般返回两个值,一个是真正的返回结果,第二个是错误信息。这些值可以通过在函数名称后面添加圆括号来返回。

package main
import "fmt"

func add(a int,b int) int  {
	return a+b
}

func add2(a,b int) int {
	return a+b
}

func exists(m map[string]string,k string) (v string,ok bool)  {
	v,ok = m[k]
	return v,ok//v就是结果,ok则是错误信息,是布尔类型
}

func main() {
	res := add(1,2)
	fmt.Println(res)//3
	
	v,ok := exists(map[string]string{"a":"A"},"a")
	fmt.Println(v,ok)//A true
}

指针

指针是指向一个变量的地址的变量。指针存储了变量的内存地址,通过指针来访问和更改变量的值。使用 & 符号,取地址运算符。

package main

import "fmt"

func add2(n int) {
//传值调用,也就是说,创建变量的副本,不是本身
	n += 2//这是无效的
}

func add2ptr(n *int) {
//指针指向的是 main 中的 n 变量
	*n += 2
}

func main() {
	n := 5
	add2(n)
	fmt.Println(n)//5
	add2ptr(&n)
	fmt.Println(n)//7
}

结构体

结构体是带类型字段集合,使用关键字typestruct

package main

import "fmt"
type user struct {
	name     string
	password string
}

func main() {
	a := user{name: "Tom", password: "2672"}
	b := user{"Lily", "2342"}
	c := user{name: "Marry"}
	c.password = "3453"
	var d user
	d.name = "Dive"
	d.password = "2357"

	fmt.Println(a, b, c, d)
        //{Tom 2672} {Lily 2342} {Marry 3453} {Dive 2357}
	fmt.Println(checkPassword(a, "456"))//false
	fmt.Println(checkPassword2(&a, "456"))//false
        
        fmt.Println(a.name)
}

func checkPassword(u user, password string) bool {
	return u.password == password
}

func checkPassword2(u *user, password string) bool {
	return u.password == password
}

结构体方法

结构体可以定义方法,这些方法可以通过结构体实例调用。

package main

import "fmt"

type user struct {
	name     string
	password string
}

func (u user) checkPassword(password string) bool {
	return u.password == password
}

func (u *user) resetPassword(password string) {
	u.password = password
}

func main() {
	a := user{name: "Tom", password: "2672"}
	fmt.Println(a)//{Tom 2672}
	a.resetPassword("2345")
	fmt.Println(a)//{Tom 2345}
        fmt.Println(a.checkPassword("2345")) //true
}

错误处理

package main

import (
	"errors"
	"fmt"
)

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")
}

func main() {
	u, err := findUser([]user{{"Tom", "2672"}}, "Tom")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) 

	if u, err := findUser([]user{{"Tom", "2672"}}, "li"); err != nil {
		fmt.Println(err) //not found
		return
	} else {
		fmt.Println(u.name)
	}
}

字符串操作

package main

import (
	"fmt"
	"strings"
)

func main() {
	a := "hello"
    //判断该字符串中是否包含另一个字符串
	fmt.Println(strings.Contains(a, "ll"))                //true
    //字符串计数
	fmt.Println(strings.Count(a, "l"))                    //2
    //接受一个字符串和一个前缀作为参数,并判断该字符串是否以该前缀开头。如果是,函数返回 true,否则返回 false
	fmt.Println(strings.HasPrefix(a, "he"))               //true
    //接受一个字符串和一个后缀作为参数,并判断该字符串是否以该后缀结尾。如果是,函数返回 true,否则返回 false
	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
	b := "你好"
    //获取字符串的长度,一个中文会对应多个字符
	fmt.Println(len(b)) //6
}

字符串格式化

在Go语言中,占位符可以是%d, %s, %f, %v%+v%#vfmt包中使用的两个格式化动词。

package main

import "fmt"

type point struct {
	x, y int
}

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

    //%v可以打印任何变量,数字用:%d,字符串用:%s,获取更加详细的结果:%+v,在详细上进一步详细:%#v
	fmt.Printf("s=%v\n", s)  //s=hello
	fmt.Printf("s=%v\n", n)  //s=123
	fmt.Printf("s=%v\n", p)  //s={1 2}
	fmt.Printf("s=%+v\n", p) //s={x:1 y:2}
	fmt.Printf("s=%#v\n", p) //s=main.point{x:1, y:2}

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

JSON处理

package main

import (
	"encoding/json"
	"fmt"
)
type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

func main() {
	a := userInfo{Name: "Tom", Age: 17, 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":"Tom","age":17,"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:"Tom", Age:17, Hobby:[]string{"Golang","TypeScript"}}
}

时间处理

使用time包,time.Now()获取当前时间,sub进行减法计算时间。

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Println(now) 
	t := time.Date(2023, 1, 15, 10, 0, 0, 0, time.UTC)
	t2 := time.Date(2023, 1, 15, 23, 59, 59, 59, time.UTC)
	fmt.Println(t)                                                  //2023-01-15 10:00:00 +0000 UTC
	fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) //2023 January 15 10 0
	
	fmt.Println(t.Format("2006-01-02 15:04:05")) //2023-01-15 10:00:00
    diff := t2.Sub(t)                          
	fmt.Println(diff)                            //13h59m59.000000059s
	fmt.Println(diff.Minutes(), diff.Seconds())  //839.9833333343166 50399.000000059
	t3, err := time.Parse("2006-01-02 15:04:05", "2023-01-15 10:00:00")
	if err != nil {
		panic(err)
	}
	fmt.Println(t3 == t)    //true
	fmt.Println(now.Unix()) //1673774280	
}

数字解析

解决字符串与数字之间的转化。

package main

import (
	"fmt"
	"strconv"
)

func main() {
	f, _ := strconv.ParseFloat("1.234", 64)
	fmt.Println(f) //1.234

    n, _ := strconv.ParseInt("111", 10, 64)//字符串 10进制(0则自动推测) 64位进度整数
	fmt.Println(n) //111

	n, _ = strconv.ParseInt("0x1000", 0, 64)
	fmt.Println(n) //4096

	n2, _ := strconv.Atoi("123")//字符串转数字
	fmt.Println(n2) //123

	n2, err := strconv.Atoi("AAA")//输入不合法则返回错误
	fmt.Println(n2, err) //0 strconv.Atoi: parsing "AAA": invalid syntax
}

进程信息

在go,用os.argv来得到程序执行的时候的指定的命令行参数。编译的一个二进制文件,command。后面接abcd来启动,输出就是os.argv会是一个长度为5的切片,第一个成员代表二进制自身的名字。用so.getenv来读取环境变量。

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	fmt.Println(os.Args)
	fmt.Println(os.Getenv("PATH"))
	fmt.Println(os.Setenv("AA", "BB"))

	buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf))
}

4.实战示例

介绍了三个项目:猜谜游戏,在线词典,以及SOCKS5代理。
猜谜游戏侧重于随机数生成时使用种子才能生成不同数字。在线词典侧重于抓包以及解析。SOCKS5代理主要侧重于理论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值