package main // 这里需要是主包
// 导入时用小括号扩起
import (
"fmt"
)
// 当然对于单个包时可以直接写明的
// import "fmt"
// 主函数即是入口,下面的格式化输出并且会发生换行
func main() {
fmt.Println("hello world")
// fmt.Println("hello world");go语言最后是不用写分号的,但是写了也可以正常运行,
// 因为编译器是会自动帮我们做这些事情的
}
package main
// 导入多个包需要分分行,用小括号扩起
import (
"fmt"
"math"
)
// 如果需要放置在同一行则需要加分号
// import (
// "fmt"; "math"
// )
func main() {
// 变量用 var 标识 和 js 倒是相像 会根据初值决定变量类型
var a = "initial"
var b, c int = 1, 2
var d = true
// 显示的说明是64位浮点数 类型说明是在后面放置 和以前学的c和java等语言不同 e的初始值应该为0
var e float64
// 将变量e转换为float32类型,并将结果赋值给变量f。由于e的值为0,因此f的值也为0。
f := float32(e)
// 将字符串"foo"拼接到字符串a的后面,并将结果赋值给变量g。因此g的值为"initialfoo"。
g := a + "foo"
fmt.Println(a, b, c, d, e, f) // initial 1 2 true 0 0
fmt.Println(g) // initialapple
// 常量定义
const s string = "constant"
const h = 500000000
// 科学计数法 3*10^20
const i = 3e20 / h
// math.Sin 计算正弦值
fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
package main
import "fmt"
func main() {
// 在Go语言中,:= 是一个简短声明变量的语法。它可以替代传统的 var 关键字和类型声明,让代码更加简洁易读。例如:
// x := 10 // 等价于 var x int = 10
// y := "hello" // 等价于 var y string = "hello"
// 需要注意的是,使用 := 声明变量时,必须为变量赋初值,否则会导致编译错误。
i := 1
// for循环 break用于跳出循环
for {
fmt.Println("loop")
break
}
// 带条件的for循环
for j := 7; j < 9; j++ {
fmt.Println(j)
}
for n := 0; n < 5; n++ {
if n%2 == 0 {
// continue语句用于回到条件判断处 跳过后面的语句
continue
}
fmt.Println(n)
}
// 条件的另一种写法
for i <= 3 {
fmt.Println(i)
i = i + 1
}
}
package main
import "fmt"
func main() {
// if条件判断语句
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
// 这段代码定义了一个名为num的变量,并使用if语句对其进行判断。
// 在if语句中可以使用简短声明变量的方式来为变量赋初值,这种方式也被称为“短声明”。
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
}
package main
import (
"fmt"
"time"
)
func main() {
// 条件分支语句 不需要像c一样每个有break
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 or five")
default:
fmt.Println("other")
}
// t := time.Now():声明一个名为t的变量,并将其初始化为当前时间。
t := time.Now()
fmt.Println(t)
switch {
// 获取小时数
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
package main
import "fmt"
func main() {
// 整形 数组 大小为5
var a [5]int
a[4] = 100
// 初值为0
fmt.Println("get:", a[2])
// len(数组名) 获取数组长度
fmt.Println("len:", len(a))
// 中间可以加空格也可以不加 确实不加更好看
b := [5]int{1, 2, 3, 4, 5}
fmt.Println(b)
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
// 对二维数组进行len计算长度计算的是行长度 2
fmt.Println(len(twoD))
// 二维数组的行计算长度进行len计算得到的是列长度 3
fmt.Println(len(twoD[0]))
}
package main
import "fmt"
func main() {
// 使用make创建切片 用起来和数组挺像 但是不同于数组,它是可拓展的
s := make([]string, 3)
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("get:", s[2]) // c
fmt.Println("len:", len(s)) // 3
// 这里便是特别之处 我们使用append方法向s后添加新元素
// 注意:append返回的是新的数组的指针 所以如果是要修改原来的内容记得赋值给自己
s = append(s, "d")
// 也可一次添加多个元素
s = append(s, "e", "f")
fmt.Println(s) // [a b c d e f]
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]
fmt.Println(s[2:]) // [c d e f]
good := []string{"g", "o", "o", "d"}
fmt.Println(good) // [g o o d]
}
go中map映射是无序的
package main
import "fmt"
func main() {
// map映射 键:值对 也是最常使用的数据结构
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
// 这里尝试获取到一个未知的键值 返回两个值 一个是键对应的值 一个是是否存在(存在则true不存在则false)
r, ok := m["unknow"]
fmt.Println(r, ok) // 0 false
// 删除对应键的数据
delete(m, "one")
// map 定义以及初始化
m2 := map[string]int{"one": 1, "two": 2}
var m3 = map[string]int{"one": 1, "two": 2}
fmt.Println(m2, m3)
}
使得数组遍历时更为简单
package main
import "fmt"
func main() {
// 对于一个 slice 或者一个 map 的话,我们可以用 range 来快速遍历,这样代码能够更加简洁。
// range 遍历的时候,对于数组会返回两个值,第一个是索引,第二个是对应位置的值。
// 如果我们不需要索引的话,我们可以用下划线来忽略。
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"}
for _, v := range m {
fmt.Println(v) // A; B
}
for k := range m {
fmt.Println("key", k) // key a; key b
}
}
package main
import "fmt"
// 函数 func 关键字标识 接下来是函数名 参数 返回值(特别的返回值可以是多个
func add(a int, b int) int {
return a + b
}
func add2(a, b int) int {
return a + b
}
// 大多是go函数都是会返回多个值的 第一个是正常的 第二个是错误信息
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return 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"
// 指针 我们首次见它是在c语言中
// 作为灵活易出错的家伙在go中却没有那么让人讨厌 它的用途被压缩了
func add2(n int) {
n += 2
}
// 修改传入参数的问题 c语言里也是很经典的 不用指针的话会有新的副本产生 不会影响到原有的内容
func add2ptr(n *int) {
*n += 2
}
func main() {
n := 5
add2(n)
fmt.Println(n) // 5
add2ptr(&n)
fmt.Println(n) // 7
}
package main
import "fmt"
// type 名字 struct
type user struct {
name string
password string
}
func main() {
// 可以见得有四种方式进行定义 我更喜欢第二种和第四种
a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}
// 用点号(.)可以实现获取结构体的内容
c.password = "1024"
var d user
d.name = "wang"
d.password = "1024"
fmt.Println(a, b, c, d) // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
fmt.Println(checkPassword(a, "1024")) // true
// 使用指针的好处在于避免了无意义的拷贝复制 减少了开销
fmt.Println(checkPassword2(&a, "haha")) // false
}
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和函数名中间即可将其定义为结构体方法 对应的结构体实例可以用点号去使用该方法
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: "wang", password: "1024"}
a.resetPassword("2048")
fmt.Println(a.checkPassword("2048")) // 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{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "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
// 是否以子字符串开头
fmt.Println(strings.HasPrefix(a, "he")) // true
// 是否以子字符串结尾 Suffix - n.后缀 v.加…作后缀;把…附在后头
fmt.Println(strings.HasSuffix(a, "llo")) // true
// 子字符串"首次"出现的位置 返回下标
fmt.Println(strings.Index(a, "ll")) // 2
// 将字符串切片{"he", "llo"}用连字符"-"连接成一个新字符串,返回结果为"he-llo"。
fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
// 将字符串a重复两次
fmt.Println(strings.Repeat(a, 2)) // hellohello
// 将字符串a中所有字符"e"替换为字符"E"
fmt.Println(strings.Replace(a, "e", "E", -1)) // hEllo
// 按照分隔符"-"将字符串"a-b-c"分割成一个字符串切片
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
// 一个中文字符占三个长度 一个英文字符占一个长度
b := "你好"
fmt.Println(len(b)) // 6
}
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打印任意类型的变量
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
}
package main
import (
"encoding/json"
"fmt"
)
// 首先保证字段名首字母大写
type userInfo struct {
Name string
Age int `json:"age"`
Hobby []string
}
func main() {
a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
// json.Marshal函数将a对象编码成JSON格式的字节数组buf。如果编码过程中出现错误,则会抛出panic异常。
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"]}
// json.MarshalIndent函数对a对象进行编码,指定缩进格式为制表符("\t")。如果编码过程中出现错误,则会抛出panic异常。
buf, err = json.MarshalIndent(a, "", "\t")
if err != nil {
panic(err)
}
fmt.Println(string(buf))
var b userInfo
// json.Unmarshal函数将buf字节数组 解码/反序列化 成一个userInfo类型的变量b。如果解码过程中出现错误,则会抛出panic异常。
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"}}
}
package main
import (
"fmt"
"time"
)
func main() {
// 获取当前时间
now := time.Now()
fmt.Println(now) // 2023-07-30 21:05:59.8593027 +0800 CST m=+0.003543001
// 自定义时间
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
// t2和t的时间差
diff := t2.Sub(t)
fmt.Println(diff) // 1h5m0s
// diff是一个time.Duration类型的对象,可以使用其Minutes()和Seconds()方法分别获取分钟数和秒数,并打印出来。
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900
// 使用time.Parse函数将字符串"2022-03-27 01:25:36"解析为一个time.Time类型的对象t3。
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
// 使用time.Now().Unix()函数获取当前时间的Unix时间戳(以秒为单位),并将其打印出来。
fmt.Println(now.Unix()) // 1690723167
}
package main
import (
"fmt"
"strconv"
)
func main() {
// 转换字符串到数字
// 64位浮点数
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234
// 十进制 64位整形
n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) // 111
/*
这段代码将字符串"0x1000"解析为一个十六进制数,并将其转换为int64类型的数字。
由于第二个参数指定了进制数为0,所以需要在字符串前面加上"0x",表示十六进制数。
同时,第三个参数指定了基数为16,表示解析的是十六进制数。最终得到的n的值为4096。
*/
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
}
package main
import (
"fmt"
"os"
"os/exec"
)
// 环境变量获取和设置 外部命令的执行
// go run example/20-env/main.go a b c d
func main() {
// 获取命令行参数
fmt.Println(os.Args) // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]
// 通过调用os.Getenv函数获取名为"PATH"的环境变量,并将其打印出来。
fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...
// 通过调用os.Setenv函数将名为"AA"的环境变量设置为"BB",并立即生效。
fmt.Println(os.Setenv("AA", "BB"))
/*
使用exec.Command函数创建一个名为"grep"的外部命令,并传入参数"127.0.0.1"和"/etc/hosts"。
然后,调用CombinedOutput方法执行该命令,并将输出结果存储在buf中。如果执行过程中出现错误,则会抛出一个panic异常。
最后,将buf转换为字符串并打印出来,得到的结果是"127.0.0.1 localhost"。
*/
// 这里实际上会报错 因为我这会儿的系统是win10 而不是linux
// buf, err := exec.Command("grep", "127.0.0.1", "/etc/hosts").CombinedOutput()
// 换成本机的显示conda版本号的命令则是成功的
buf, err := exec.Command("conda", "--version").CombinedOutput()
if err != nil {
panic(err)
}
fmt.Println(string(buf)) // 127.0.0.1 localhost
}
package main
// Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
// goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
import (
"fmt"
"time"
)
func say(s string) {
for i:= 0; i < 5; i++ {
time.Sleep(100*time.Millisecond)
fmt.Println(s)
}
}
func main() {
// 使用 go 语句开启一个新的运行期线程
// 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。
// 同一个程序中的所有 goroutine 共享同一个地址空间。
go say("world")
say("hello")
}
package main
// 通道(channel)是用来传递数据的一个数据结构。
// 通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。
// 操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
/*
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据并把值赋给 v
*/
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _,v := range s {
sum += v
}
c <- sum
}
func main() {
s := []int{1,2,3,4,5,6}
// 声明通道的方式 --默认是不带有缓冲区的 发送端发送数据,同时必须有接收端相应的接收数据。
// c := make(chan int)
// 带缓冲区 缓冲区可以暂存数据
c := make(chan int, 10)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:],c)
x, y := <-c, <-c
fmt.Println(x, y, x+y)
}
/*
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
// 会结束,从而在接收第 11 个数据的时候就阻塞了。
for i := range c {
fmt.Println(i)
}
}
*/
控制读写权限
go func(c chan int) { //读写均可的channel c } (a)
go func(c <- chan int) { //只读的Channel } (a)
go func(c chan <- int) { //只写的Channel } (a)
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。
Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。
package main
import "fmt"
type Phone interface {
call()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("我是nok,我在叫你")
}
type IPhone struct {
}
func (iPhone IPhone) call() {
fmt.Println("我是iPhone,我在叫你")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}