当前位置:网站首页>Go 学习笔记

Go 学习笔记

2022-06-22 04:05:00 Looooking

最近一直接触的是 Python, Ruby 之类的解释性语言,至于静态语言和动态语言的优劣,这儿就不多分析了。如果现阶段想要熟练掌握一门静态语言,感觉 Go 应该是不错的选择,如果有 C++ 、Java 的语言基础,相信上手应该也会很快。

参考:Go入门指南 · the way to go

基本结构和数据类型

关键字和标识符

Go 代码中会使用到的关键字:

breakdefaultfuncinterface
casedefergomap
chanelsegotopackage
constfallthroughifrange
continueforimportreturn

Go 代码中会使用到的标识符:

appendboolbytecapclosecomplex
copyfalsefloat32float64imagint
int32int64iotalenmakenew
printprintlnrealrecoverstringtrue

基本结构和要素 

可见性规则

当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。

Hello World

package main

import "fmt"

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

注释

注释和 C++ 的一样,可以使用 // 进行单行注释,可以使用 /* ... */ 进行块注释。

类型及转换

使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。

类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。

在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换,因此所有的转换都必须显式说明,就像调用一个函数一样。

package main

import "fmt"

func main() {
	a := 5.0
	b := int(a)
	fmt.Println(b)
}

一般结构

  • 在完成包的 import 之后,开始对常量、变量和类型的定义或声明。
  • 如果存在 init 函数的话,则对该函数进行定义(这是一个特殊的函数,每个含有该函数的包都会首先执行这个函数)。
  • 如果当前包是 main 包,则定义 main 函数。
  • 然后定义其余的函数,首先是类型的方法,接着是按照 main 函数中先后调用的顺序来定义相关函数,多个函数,可以按照字母顺序来进行排序。

常量

常量的值必须是能够在编译时就能够确定的。因为在编译期间自定义函数均属于未知,因此无法用于常量的赋值,但内置函数可以使用。

常量定义

语法:

const identifier [type] = value

常量使用关键字 const 定义,用于存储不会改变的数据。存储在常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

const Pi = 3.14159

隐式与显式

编译器可以根据变量的值来推断其类型。

  • 显式类型定义: const b string = "abc"

  • 隐式类型定义: const b = "abc"

并行赋值

package main

import "fmt"

const beef, two = "meet", 2
const (
	Monday, Tuesday, Wednesday = 1, 2, 3
	Thursday, Friday, Saturday = 4, 5, 6
)
const (
	Unknown = 0
	Male = 1
	Female = 2
)

const (
	RED int = iota
	ORANGE
	YELLOW
	GREEN
	BLUE
	INDIGO
	VIOLET
)

func main() {
	fmt.Println(Wednesday)
	fmt.Println(Male)
	fmt.Println(YELLOW)
}

变量

声明

Go声明变量时将变量的类型放在变量的名称之后,声明形式相比 C 而言更加清晰。

语法:

var identifier type

当一个变量被声明之后,系统自动赋予它该类型的零值:int 为 0,float 为 0.0,bool 为 false,string 为空字符串,指针为 nil。

package main

import "fmt"

var (
	a int
	b bool
	c string
)
func main() {
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}
0
false

赋值

一般情况下,只有类型相同的变量之间才可以相互赋值。声明与赋值(初始化)语句也可以组合起来。

package main

import "fmt"

var (
	a int		= 1
	b bool		= true
	c string	= "hello"
)
func main() {
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
}
1
true
hello

简短赋值

简短赋值是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明

如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明。

如果声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误。

package main

import "fmt"

func main() {
	var a int = 1
	b := 2
	fmt.Println(a)
	fmt.Println(b)
}

init 函数

package main

import (
	"fmt"
	"math"
)

var Pi float64
func init()  {
	Pi = 4 * math.Atan(1)
}
func main() {
	fmt.Println(Pi)
}

基本类型

布尔类型

布尔型的值只可以是常量 true 或者 false。两个类型相同的值可以使用相等 == 或者不等 != 运算符来进行比较并获得一个布尔型的值。

Go 对于值之间的比较有非常严格的限制,只有两个类型相同的值才可以进行比较。像下面这种是会报错的哟。

package main

import "fmt"

func main() {
	a := true
	b := "hello"
	if a == b {
		fmt.Println("a equal to b")
	}
}
# command-line-arguments
.\test.go:8:7: invalid operation: a == b (mismatched types bool and string)

Compilation finished with exit code 2

&& 和 || 是具有快捷性质的运算符(大部分语言都是有这个特性的,比如 Ruby,或者 Python 的 and 和 or),当运算符左边表达式的值已经能够决定整个表达式的值的时候(&& 左边的值为 false,|| 左边的值为 true),运算符右边的表达式将不会被执行。利用这个性质,如果你有多个条件判断,应当将计算过程较为复杂的表达式放在运算符的右侧以减少不必要的运算。

数字类型

Go 语言支持整型和浮点型数字,包括基于架构的类型。例如:int、uint 和 uintptr。

与操作系统架构无关的类型都有固定的大小,并在类型的名称中就可以看出来:

整数:

  • int8(-128 -> 127)
  • int16(-32768 -> 32767)
  • int32(-2,147,483,648 -> 2,147,483,647)
  • int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)

无符号整数:

  • uint8(0 -> 255)
  • uint16(0 -> 65,535)
  • uint32(0 -> 4,294,967,295)
  • uint64(0 -> 18,446,744,073,709,551,615)

浮点型(IEEE-754 标准):

  • float32(+- 1e-45 -> +- 3.4 * 1e38)
  • float64(+- 5 * 1e-324 -> 107 * 1e308)

Go 中不允许不同类型之间的混合使用(除非通过显式转换),但是对于常量的类型限制非常少,因此允许常量之间的混合使用。

package main

func main() {
	var a int
	var b int32
	a = 15
	b = b + a    // 编译错误
	b = b + 5    // 因为 5 是常量,所以可以通过编译
}

在格式化字符串里,%t 来表示你要输出的值为布尔型,%d 用于格式化整数(%x 和 %X 用于格式化 16 进制表示的数字),%g 用于格式化浮点型(%f 输出浮点数,%e 输出科学计数表示法),%0d 用于规定输出定长的整数,其中开头的数字 0 是必须的。%n.mg 用于表示数字 n 并精确到小数点后 m 位,除了使用 g 之外,还可以使用 e 或者 f,例如:使用格式化字符串%5.2e 来输出 3.4 的结果为 3.40e+00

关于更多 Go 格式化输出的内容,可参考:Go语言格式化输出_General_zy的博客-CSDN博客_go 格式化输出

数字值转换

当进行类似 a32bitInt = int32(a32Float) 转换时,小数点后的数字将被丢弃。这种情况一般发生当从取值范围较大的类型转换为取值范围较小的类型时。

字符类型

在 Go 中,字符只是整数的特殊用例。byte 类型是 uint8 的别名,对于只占用 1 个字节的传统 ASCII 编码的字符来说,完全没有问题。例如:var ch byte = 'A'字符使用单引号括起来。格式化说明符 %c 用于表示字符;当和字符配合使用时,%v 或 %d 会输出用于表示该字符的整数;%U 输出格式为 U+hhhh 的字符串。

var ch byte = 65 或 var ch byte = '\x41'

字符串

解释字符串

该类字符串使用双引号括起来,其中的相关的转义字符将被替换,这些转义字符包括:

  • \n:换行符
  • \r:回车符
  • \t:tab 键
  • \u 或 \U:Unicode 字符
  • \\:反斜杠自身

非解释字符串

该类字符串使用反引号(其他解释型语言会使用单引号)括起来,例如:

`This is a raw string \n` 中的 `\n\` 会被原样输出。
package main

import "fmt"

func main() {
	fmt.Println("hello\nworld")
	fmt.Println(`hello\nworld`)
}
hello
world
hello\nworld

len() 来获取字符串所占的字节长度,而不是字符串的长度!!!(字符串长度获取另有其方法)

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	str1 := "asSASA ddd dsjkdsjsこん dk"
	fmt.Println(len(str1))
	fmt.Println(utf8.RuneCountInString(str1))
}
28
24

字符串拼接

+ 号 或者 strings.Join()

strings

前后缀

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.HasPrefix("hello world", "hello"))
	fmt.Println(strings.HasSuffix("hello world", "world"))
}
true
true

包含关系

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.Contains("hello world", "o w"))
}
true

字符串索引

Indx、LastIndex

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.Index("hello world", "o"))
	fmt.Println(strings.LastIndex("hello world", "o"))
}
4
7

字符串替换

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.Replace("hello world", "l", "x", 2))
	fmt.Println(strings.Replace("hello world", "l", "x", -1))
}
hexxo world
hexxo worxd

 字符串统计

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.Count("hello world", "l"))
}
3

字符串重复

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.Repeat("hello ", 3))
}
hello hello hello

 字符串大小写

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.ToLower("Hello World"))
	fmt.Println(strings.ToUpper("Hello World"))
}
hello world
HELLO WORLD

 字符串修剪

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.TrimSpace("  Hello World  "))
	fmt.Println(strings.TrimLeft("  Hello World  ", " "))
	fmt.Println(strings.TrimRight("  Hello World  ", " "))
	fmt.Println(strings.Trim("Hello World", "old"))
}
Hello World
Hello World  
  Hello World
Hello Wor

字符串分割

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.Fields("hello world"))
	fmt.Println(strings.Fields("hello   world"))	// 类似于 Ruby 的 split
	fmt.Println(strings.Split("hello world", " "))
	fmt.Println(strings.Split("hello   world", " ")) // 类似于 Python 的 split(" ")
													 // "hello   world".split(" ")
													 // ['hello', '', '', 'world']
}
[hello world]
[hello world]
[hello world]
[hello   world]

字符串拼接

package main

import (
	"fmt"
	"strings"
)

func main() {
	str1 := strings.Fields("hello world")
	fmt.Println(strings.Join(str1, "-"))
}
hello-world

字符串读取

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Println(strings.NewReader("hello world"))
}
&{hello world 0 -1}

strconv

package main

import (
	"fmt"
	"strconv"
)

func main() {
	var orig string = "666"
	var an int
	var newS string

	fmt.Printf("The size of ints is: %d\n", strconv.IntSize)

	an, _ = strconv.Atoi(orig)
	fmt.Printf("The integer is: %d\n", an)
	an = an + 5
	newS = strconv.Itoa(an)
	fmt.Printf("The new string is: %s\n", newS)
}
The size of ints is: 64
The integer is: 666
The new string is: 671

时间和日期

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	fmt.Println(t.YearDay())
	fmt.Println(t.Year())
	fmt.Println(t.Month())
	fmt.Println(t.Day())
	fmt.Println(t.Date())
	fmt.Println(t.Unix())
	fmt.Println(t.Format(time.RFC1123))
	fmt.Println(t.Format("2006-01-02 03:04:05"))
}
171
2022
June
20
2022 June 20
1655693632
Mon, 20 Jun 2022 10:53:52 CST
2022-06-20 11:05:39

指针

一个指针变量可以指向任何一个值的内存地址 它指向那个值的内存地址,在 32 位机器上占用 4 个字节,在 64 位机器上占用 8 个字节,并且与它所指向的值的大小无关。Go 语言中的指针保证了内存安全。

package main

import "fmt"

func main() {
	var i int = 5
	var ptr *int
	ptr = &i
	fmt.Println(i, *ptr, ptr)
	*ptr = 6 // 通过 *ptr 可以修改原始对象的值
	fmt.Println(i, *ptr, ptr)
}
5 5 0xc00000a098
6 6 0xc00000a098

控制结构

关键字 if 和 else 之后的左大括号必须和 else-if 关键字在同一行。这两条规则都是被编译器强制规定的。

if-else

if

if condition {
    // do something 
}

if-else

if condition {
    // do something 
} else {
    // do something 
}

if-else if

if condition1 {
    // do something 
} else if condition2 {
    // do something else    
}else {
    // catch-all or default
}

if initialization; condition

这种写法具有固定的格式(在初始化语句后方必须加上分号)。

if initialization; condition {
    // do something
}

如果变量在 if 结构之前就已经存在,那么在 if 结构中,该变量原来的值会被隐藏(局部变量优先原则)。

package main

import (
	"fmt"
)

func main() {
	var i int = 10
	if i := 5; i == 5 {
		fmt.Println("i ==", i)
	} else {
		fmt.Println("i != 5")
	}
	fmt.Println("i ==", i)
}
i == 5
i == 10

函数返回值

Go 语言的函数经常使用两个返回值来表示执行是否成功:返回某个值以及 true 表示成功;返回零值(或 nil)和 false 表示失败。当不使用 true 或 false 的时候,也可以使用一个 error 类型的变量来代替作为第二个返回值:成功执行的话,error 的值为 nil,否则就会包含相应的错误信息(Go 语言中的错误类型为 error: var err error)。这样一来,就很明显需要用一个 if 语句来测试执行结果;由于其符号的原因,这样的形式又称之为 comma,ok 模式(pattern)。

normal

package main

import (
	"fmt"
	"strconv"
)

func main() {
	var orig string = "666"
	// var an int
	var newS string
	// var err error

	fmt.Printf("The size of ints is: %d\n", strconv.IntSize)
	// anInt, err = strconv.Atoi(origStr)
	an, err := strconv.Atoi(orig)
	if err != nil {
		fmt.Printf("orig %s is not an integer - exiting with error\n", orig)
		return
	}
	fmt.Printf("The integer is %d\n", an)
	an = an + 5
	newS = strconv.Itoa(an)
	fmt.Printf("The new string is: %s\n", newS)
}
The size of ints is: 64
The integer is 666
The new string is: 671

error

package main

import (
	"fmt"
	"strconv"
)

func main() {
	var orig string = "ABC"
	// var an int
	var newS string
	// var err error

	fmt.Printf("The size of ints is: %d\n", strconv.IntSize)
	// anInt, err = strconv.Atoi(origStr)
	an, err := strconv.Atoi(orig)
	if err != nil {
		fmt.Printf("orig %s is not an integer - exiting with error\n", orig)
		return
	}
	fmt.Printf("The integer is %d\n", an)
	an = an + 5
	newS = strconv.Itoa(an)
	fmt.Printf("The new string is: %s\n", newS)
}
The size of ints is: 64
orig ABC is not an integer - exiting with error

习惯用法

if err := file.Chmod(0664); err !=nil {
    fmt.Println(err)
    return err
}

//------------------------------------
if value, ok := readData(); ok {
…
}
//------------------------------------
func atoi (s string) (n int) {
	//1、如果返回多个值是,在接受时,希望忽略某个返回值,则使用_符号占位忽略。
	//2、如果返回值只有一个(返回值类型列表)可以不写()
    n, _ = strconv.Atoi(s)
    return
}

原网站

版权声明
本文为[Looooking]所创,转载请带上原文链接,感谢
https://blog.csdn.net/TomorrowAndTuture/article/details/125273863