Golang 学习笔记

从Python转过来蛮不容易的,基础放一段时间不用就忘,好菜

基础语法

HelloWorld

package main

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

/* output
Hello World.
*/

变量创建与赋值

var name = "Golang" // 普通写法
age := 11 // 简化写法

var num int // 提前声明
num = 1

/* 连续创建与赋值 */
var a,b,c int
a,b,c = 1,2,3

常量

const pi = 3.1415926

运算符

基本上和其他语言差异不大 加减乘除求余自增减 相等不等大小与大小等于 blabla…

循环和条件

func main() {
	for i := 1; i != 100; i++ {
		fmt.Println(i)
	}

	var i int = 1
	for {
		i++
		// 条件
		if i == 100 {
			break
		} else {
			continue
		}
	}

	// 死循环
	for {
		fmt.Println("loop")
	}
}

遍历对象

通过for i,v := range x 遍历x切片,其中i为下标index,v为内容value。使用_可忽略不创建下标变量。

func main() {
	Say("Go", "Python", "Ruby")

	var s = []string{"Go", "Python", "Ruby"}
	for _, v := range s {
		fmt.Println(v)
	}
}

/* output
Go
Python
Ruby
*/

函数

函数参数的接收和返回都需要指定类型

// 函数名首字母大小写决定了外部是否能够调用该函数,首字母大写时才允许外部调用
func Say(msg string) string {
	return "Hello " + msg
}

func main() {
	fmt.Println(Say("Go"))
}

/* output
Hello Go
*/

语法糖

函数通过…可接收N个参数

func Say(msg ...string) {
	for _, m := range msg {
		fmt.Println("Hi " + m)
	}
}

func main() {
	Say("Go", "Python", "Ruby")
}

指针

好难呀

func main() {
	var s = "A"
	var sa = &s
	fmt.Println(sa)
 // sa保存着s的内存地址
	fmt.Println(*sa)
 // 通过地址获取存储的值
}

/* output
0xc0000881e0
A
*/

指针数组 -> 存放指针的数组

数组指针 -> 指向数组的指针

类型转换

// 才发现string()转换int的时候用的Unicode
func main() {
	var s = 65
	fmt.Println(string(s))
}

/* output
A
*/

常用类型

切片 Slice

var s1 = []int{1, 2, 3}
var s2 = make([]int, 3) // 长度3的切片
s2 = append(s2, 1)
fmt.Println(s1)
fmt.Println(s2)

/* output
[1 2 3]
[0 0 0 1]  // append会扩展切片长度,事先声明的键值为0
*/

切片的其他操作和Python列表类似,比如取范围内的值

var s1 = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(s1[:5])

/* output
[0 1 2 3 4] // 取得前五位
*/

遗憾的是Go切片没法像Python那样灵活,比如Python里的取最后一位是这样[-1:],Go是没法实现的

var s1 = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(s1[-1:])

/* output
.\main.go:7:16: invalid slice index -1 (index must be non-negative)
*/

集合 Map

类似Python的字典Dict,Go没有Python那样Set去重,但这里的Map可以用来去重

// 通过map把s切片去重后放入result切片
func main() {
	var s = []string{"a", "b", "c", "d", "d", "d", "d", "d"}
	var result []string
	var m = make(map[string]bool)
	for _, v := range s {
		if _, ok := m[v]; ok == false {
			m[v] = true
			result = append(result, v)
		}
	}
	fmt.Println(result)
}

结构体 Struct

开始有点摸不着头脑,后来多写几次就明白了些

// 定义结构体,包含两个属性
type Kid struct {
	Age  int
	Name string
}

// Kid结构体拥有的方法
func (k Kid) Say(msg string) string {
	return k.Name + " say " + msg
}

func main() {
	var kid = Kid{Age: 1, Name: "Ami"}
	fmt.Println(kid)
	fmt.Println(kid.Say("略略略"))
}

/* output
{1 Ami}
Ami say 略略略
*/

结构体有点像Python的类,也有继承

type Human struct {
	Color string
}

type Kid struct {
	Human
 // 继承Human结构体
	Age  int
	Name string
}

// Kid结构体拥有的方法
func (k Kid) Say(msg string) string {
	return k.Name + " say " + msg
}

func main() {
	var kid = Kid{Age: 1, Name: "Ami"}
	kid.Color = "Yellow"
 // 赋值继承到的属性Color

	fmt.Println(kid)
}

/* output
{{Yellow} 1 Ami}
*/

接口 Interface

炒鸡难,开始看菜鸟教程的时候云里雾里的,现在觉得他们给的例子写的很奇怪。

/*
可以看到Kid结构体和Behavior接口一点关联都没有,除了main函数里的一次赋值
就是这样一次赋值,Behavior的实例化变量b就拥有了Eat方法
*/

type Kid struct {
	Age  int
	Name string
}

type Behavior interface {
	Eat()
}

func (k Kid) Eat() {
	fmt.Println(k.Name + " eat apple")
}

func main() {
	var kid = Kid{Age: 1, Name: "Ami"}

	var b Behavior
	b = kid
   // 这儿将kid丢给了实例化后的Behavior
	b.Eat()
   // 拥有了Eat方法
}

/* output
Ami eat apple
*/

协程Goroutine & 管道 Channel

管道通常和协程一起出现,是协程间传输数据的通道。

启动协程的方式很简单,用go关键字即可启动协程

go func()

// 协程匿名函数
go func() {
	fmt.Println("a")
}()

管道需要协程来启动,否则会报错

var c = make(chan int)
func Send() {
	c <- 1
	c <- 2
	c <- 3
}

func main() {
	Send()
}

/* output
fatal error: all goroutines are asleep - deadlock!
*/

正确写法

var c = make(chan int)

func Send() {
	c <- 1
	c <- 2
	c <- 3
}

func Get() {
	for {
		select {
		case v := <-c:
			fmt.Println(v)
		}
	}
}

func main() {
	go Send()
	go Get()
	time.Sleep(time.Second * 1)
}

/* output
1
2
3
*/

基础Plus

值传递和引用传递

值类型在赋值时变量存储的内容是数值本身

func main() {
	var a = "A"
	var b = a
 // b变量赋值了a变量中存放的"A"字符串
	fmt.Printf("a=%s b=%s\n", a, b)
	a = "B"
 // 当a改变后,b不会改变

	fmt.Printf("a=%s b=%s\n", a, b)
}

/* output
a=A b=A
a=B b=A
*/

引用类型变量存储的是数值的内存地址,也就是指针

func main() {
	var a = []string{"Hi", "Go"}
	var b = a
	fmt.Println(a, b)
	a[1] = "Golang"
	fmt.Println(a, b)
}

/* output
[Hi Go] [Hi Go]
[Hi Golang] [Hi Golang]
*/

实例

JSON序列化

type Kid struct {
	Name   string `json:"name"`
	Age    int    `json:"age"`
	Height int    `json:"height"`
}

func main() {
	var kid = Kid{Name: "Marks", Age: 10, Height: 140}
	fmt.Println(kid)

	jsonData, err := json.Marshal(kid)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(jsonData))
}

> output
{Marks 10 140}
{"name":"Marks","age":10,"height":140}

JSON反序列化

原生的JSON处理真的很麻烦,格式化一串JSON字符串需要预先写好结构体,好在有在线工具能够自动转换

type Kid struct {
	Name   string `json:"name"`
	Age    int    `json:"age"`
	Height int    `json:"height"`
}

func main() {
	var jsonData = `{"name":"Marks","age":10,"height":140}`
	var kid = new(Kid)
	fmt.Println(kid)

	err := json.Unmarshal([]byte(jsonData), &kid)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(kid)
}

> output
&{ 0 0}
&{Marks 10 140}

格式化赋值

类似与Python的format

func main() {
	var host = "sec.kim"
	var url = fmt.Sprintf("https://%s/", host)
	fmt.Println(url)
}

/* output
https://sec.kim/
*/

HTTP请求

当然还有更多高级用法,比如添加Header之类的

func main() {
	resp, err := http.Get("https://sec.kim/")
	if err != nil {
		log.Fatal(err)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(body))
}

读取管道数据流

Linux经常看到的方法,ps aux | grep ,golang也可以实现grep这样从管道符左边读取数据流

func main() {
	fi, err := os.Stdin.Stat()
	if err != nil {
		log.Fatal(err)
	}
	if fi.Mode()&os.ModeNamedPipe == 0 {
		fmt.Println("no pipe :(")
	} else {
		reader := bufio.NewReader(os.Stdin)
		str, _, err := reader.ReadLine()
		if err != nil {
			log.Println(err)
		}
		fmt.Println(string(str)+" Golang")
	}
}

/* powershell output

> go build main.go && ls
-a----         2020/12/1     14:58        2313216 main.exe
-a----         2020/12/1     14:57            357 main.go
> Write-Output Halo | .\main.exe
Halo Golang
*/
标签:

发表评论

邮箱地址不会被公开。 必填项已用*标注