• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Go入门语法 | 青训营

武飞扬头像
LoliWolf
帮助5

HelloWorld

package main

import "fmt"

func main() {
    fmt.Print("hello,world")
}

第一行 package main 代表这个文件属于 main 包的一部分,main 包也就是程序的入口包。

第三行导入了标准库里面的 FMT 包。这个包主要是用来往屏幕输入输出字符串、格式化字符串。

import 下面是 main 函数,main 函数的话里面调用了 fmt.Println 输出 helloword,fmt包是标准格式化输入输出包。

变量

package main

import (
    "fmt"
    "math"
)

func main() {

    var a = "str"
    ab := "str2"
    var b, c int = 1, 2
    var d = true
    var e float64
    f := float64(e)
    g := a   "test"
    fmt.Println(a, b, c, d, e, f, g, ab)
    fmt.Print(a, " ", b, c)
    const s = "constant"
    const h = 5000
    const i = 3e20 / h
    fmt.Println(s, h, i, math.Sin(h), math.Sin(i))
}
  • 完整的变量声明。 var name type
  • 声明并初始值 var name type = value
  • 自动类型推断 var name = value
  • 声明多变量 var name1,name2 = 'liwentong1','liwentong2'
  • 简短声明: name2 := 'string'

自动类型推断

如果变量有初始值,那么 Go 能够自动推断具有初始值的变量的类型,声明时可以省略type

变量重声明

对已声明过的变量再次声明

  • 重声明的类型与原有类型是一致的。
  • 重声明只能使用短声明的方式,进行重声明。
  • 重声明发生在当前代码快。而除此本代码块的重声明,就需考虑作用于范围。
package main
import "fmt"
func addNum(a,b int) int {
    return a b
}
 
func main(){
   var a int
   a,b := 1,2
   var c = addNum(a,b)
   if b != 0  {
      a := 32   //在此代码块中重声明,那表示在此代码块中,对a重新进行了声明。此会覆盖此代码块外的a=2的声明。并且此时a的作用域,也仅在此代码块中。
      fmt.Println(a)
   }
   fmt.Println(a) //此处的a,是外层的变量a。 a=1。
   fmt.Println(c)
}

go语言是一门强类型语言,每一个变量都有它自己的变量类型。

常见的变量类型包括 字符串 整数 浮点型、布尔型等。

go 语言的字符串是内置类型,可以直接通过加号拼接,也能够直接用等于号去比较两个字符串。

在go语言里面,大部分运算符的使用和优先级都和 C 或者 C 类似

常量就是把 var 改成const,值在一提的是go语言里面的常量,它没有确定的类型,会根据使用的上下文来自动确定类型。

空值

go中的空值为nil

选择语句

package main

import "fmt"

func main() {
    //无字符类型,直接打印成ascll码
    fmt.Println('a')
    if 7%2 == 0 {
        fmt.Println("even")
    }
    if num := 9; num < 0 {
        fmt.Println('1')
    }
}

if条件不加括号,后面的代码块必须包大括号。可以在条件语句中声明并初始化一个变量,并在同一行中对该变量进行条件判断。

循环语句

没有while,只有for循环

package main

import "fmt"

func main() {
    i := 1
    for {
        fmt.Println("ok")
        break
    }
    for j := 7; j < 9; j   {
        fmt.Println(j)
    }
    for n := 0; n < 5; n   {
        if n%2 == 0 {
            continue
        }
        fmt.Println(n)
    }
    for i <= 3 {
        fmt.Println(i)
        i = i   1
    }
}

三段for可任意省略。在 for 后面什么都不写,代表一个死循环。

分支语句

package main

import (
    "fmt"
    "time"
)

func main() {
    i := 4
    switch i {
    case 1:
        fmt.Println("1")
    case 2, 3:
        fmt.Println("2,3")
        fmt.Println("2,3")
    default:
        fmt.Println("other")
    }

    now := time.Now()
    switch {
    case now.Hour() < 12:
        fmt.Println("bf")
    default:
        fmt.Println("af")
    }
}

在c 里面, switch case 如果不加 break 的话会然后会继续往下跑完所有的 case, 在go语言里不需要加 break 。

可以使用任意的变量类型,甚至可以取代任意的 if else 语句。你可以在 switch 后面不加任何的变量,然后在 case 里面写条件分支。这样代码相比你用多个 if else 代码逻辑会更为清晰。

数组

package main

import "fmt"

func main() {
    //声明数组方式
    var a [5]int
    ints := [5]int{1, 2, 3, 4, 5}
    //可以索引访问,也可以调用函数或直接打印全部
    ints[2] = 1
    fmt.Println(ints[1], a[1])
    fmt.Println(len(a), ints)
    //多维数组及用法
    var two [2][4]int
    twoD := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}
    for i := 0; i < 2; i   {
        for j := 0; j < 4; j   {
            two[i][j] = i   j
        }
    }
    fmt.Println(two, twoD)
}

切片

切片不同于数组可以任意更改长度,然后也有更多丰富的操作

package main

import "fmt"

func main() {
    s := make([]string, 3)
    s[0] = "a"
    s[1] = "b"
    s[2] = "c"
    fmt.Println("get:", s[2])
    fmt.Println("len:", len(s)) //3
    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]
}

用 make 来创建一个切片,可以像数组一样去取值,使用 append 来追加元素。注意 append 的用法的话,你必须把 append 的结果赋值为原数组。

slice 的原理实际上是它存储了长度和容量,加指向数组的指针,在你执行 append 操作的时候,如果容量不够的话,会扩容并且返回新的 slice。

slice 此初始化的时候也可以指定长度。

slice 拥有像 python 一样的切片操作,比如这个代表取出第二个到第五个位置的元素,不包括第五个元素。不过不同于python,这里不支持负数索引

map

package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["one"] = 1
	m["two"] = 2
	fmt.Println(m)
	fmt.Println(m["one"])
	fmt.Println(m["null"])
	//结果和是否存在
	result, ok := m["null"]
	fmt.Println(result, ok)
	//删除元素
	delete(m, "one")
	//其他声明方式
	m2 := map[string]int{"one": 1, "two": 2}
	var m3 = map[string]int{"one": 1, "two": 2}
	fmt.Println(m2, m3)
}

完全无序,遍历时按随机顺序

range

可以对slice或map进行快速遍历

遍历slice时会返回两个值:index,value;遍历map时返回:key,value。可以用下划线来忽略

package main

import "fmt"

func main() {
	a := []int{11, 22, 33, 44}
	for _, v := range a {//打印index value
		fmt.Println(v)
	}
	m := map[string]string{"a": "A"}
	for s, s2 := range m {//打印key value
		fmt.Println(s, " ", s2)
	}
	for s := range m {//打印key
		fmt.Println(s)
	}
}

函数

go中变量类型是后置的

Golang 里面的函数原生支持返回多个值。在实际的业务逻辑代码里面几乎所有的函数都返回两个值,第一个是真正的返回结果,第二个值是一个错误信息。

package main

import "fmt"
//后置变量类型
func add(a int, b int) int {
	return a   b
}
//可以统一声明类型
func add2(a, b int) int {
	return add(a, b)
}

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

func main() {
	i := add2(1, 2)
	fmt.Println(i)
	fmt.Println(exists(map[string]string{"a": "A"}, "a"))
}

命名返回值:Go语言支持命名返回值。命名返回值就像在函数顶部声明的变量,函数结束时会自动返回它们的当前值。

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return
}

函数作为参数或返回值

package main

import (
	"fmt"
	"strings"
)

// 定义一个函数类型
type StringFunc func(string) string

// 将函数作为参数传入
func ApplyStrFunc(s string, f StringFunc) string {
	return f(s)
}

// 返回一个函数
func MakeAppendFunc(suffix string) StringFunc {
	return func(s string) string {
		return s   suffix
	}
}

func main() {
	// 将函数作为参数传入
	fmt.Println(ApplyStrFunc("hello", strings.ToUpper)) // 输出 "HELLO"

	// 返回一个函数并使用它
	appendWorld := MakeAppendFunc(" world")
	fmt.Println(appendWorld("hello")) // 输出 "hello world"
}

首先定义了一个StringFunc类型,它是一个接收一个字符串并返回一个字符串的函数类型。然后定义了一个ApplyStrFunc函数,它接收一个字符串和一个StringFunc类型的函数,然后将字符串传给StringFunc并返回结果。

MakeAppendFunc函数中,我们返回了一个StringFunc类型的函数,这个返回的函数会将一个后缀添加到传入的字符串上。

main函数中,先使用ApplyStrFunc函数将字符串"hello"转换为大写,然后创建了一个appendWorld函数,这个函数会在传入的字符串后添加" world",然后我们使用这个函数将"hello"转换为"hello world"

指针

go中指针的操作有限,主要用途就是对于传入参数进行修改

package main

import "fmt"

func add2np(n int) {
	n  = 2
}

func add2ptr(i *int) {
	*i  = 2
}

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

结构体

package main

import "fmt"

type user struct {
	name     string
	password string
}

func main() {
	a := user{"name", "password"}
	b := user{name: "name", password: "password"}
	c := user{name: "name"}
	c.password = "123"
	var d user
	d.name = "zfb"
	d.password = "password"

	fmt.Println(a, b, c, d)
	fmt.Println(checkPassword(a, "password"))
	fmt.Println(checkPassword2(&a, "password"))
}

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 user2 struct {
	name     string
	password string
}

func main() {
	a := user2{"name", "password"}
	b := user2{name: "name", password: "password"}
	c := user2{name: "name"}
	c.password = "123"
	var d user2
	d.name = "zfb"
	d.password = "password"

	fmt.Println(a, b, c, d)
	fmt.Println(c.checkPassword("123"))
	c.setPassword("321")
	fmt.Println(c.password)
}

func (u user2) checkPassword(password string) bool {
	return u.password == password
}
func (u *user2) setPassword(password string) {
	u.password = password
}

在Go中,可以为任何类型定义方法,包括结构体。结构体的方法定义在结构体之外,但需要在函数名前加上一个参数,这个参数代表调用该方法的结构体实例。在实现结构体方法时也有两种写法,一种是带指针,一种是不带指针。如果带指针的话,就可以对这个结构体去做修改。不带指针的话,实际上操作的是一个拷贝,就无法对结构体进行修改。

错误处理

package main

import (
	"errors"
	"fmt"
)

type user3 struct {
	name     string
	password string
}

func finduser3(user3s []user3, name string) (v *user3, err error) {
	for _, u := range user3s {
		if u.name == name {
			return &u, nil
		}
	}

	return nil, errors.New("not found")
}
func main() {
	u, err := finduser3([]user3{{"wang", "1024"}}, "wang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) //wang
	if u, err := finduser3([]user3{{"wang", "1024"}}, "q"); err != nil {
		fmt.Println(err) //not found
		return
	} else {
		fmt.Println(u.name)
	}
}

错误处理通常通过函数的多返回值来实现。函数可以返回一个错误值,如果这个值不是nil,那么就表示有错误发生。

字符串操作

package main

import (
	"fmt"
	"strings"
)

func main() {
	a := "hello"
	fmt.Println(strings.Contains(a, "llo"))
	fmt.Println(strings.Count(a, "l"))
	fmt.Println(strings.HasSuffix(a, "llo"))
	fmt.Println(strings.Index(a, "ll"))
	fmt.Println(strings.Join([]string{"he", "llo"}, " "))
	//返回将a中前n个不重叠e子串都替换为E的新字符串,如果n<0会替换所有e子串。
	fmt.Println(strings.Replace(a, "e", "E", -1))
	fmt.Println(strings.Split("1[2[3", "["))
	fmt.Println(len(a))
	fmt.Println(len("中")) //中文占3字符长
}
  1. 判断子串strings.Contains(a, "llo")函数检查字符串a("hello")是否包含子串"llo"。返回一个布尔值,如果a包含"llo",则返回true
  2. 计数子串strings.Count(a, "l")函数计算字符串a中字符"l"的数量。返回一个整数,表示字符"l"在a中的出现次数。
  3. 判断后缀strings.HasSuffix(a, "llo")函数检查字符串a是否以子串"llo"结束。返回一个布尔值,如果a以"llo"结尾,则返回true
  4. 查找子串strings.Index(a, "ll")函数查找子串"ll"在字符串a中首次出现的索引位置。返回一个整数,表示子串在a中的起始位置。如果a中不包含"ll",则返回-1。
  5. 连接字符串strings.Join([]string{"he", "llo"}, " ")函数使用空格" "将字符串切片中的所有元素连接起来,形成一个新的字符串。
  6. 替换子串strings.Replace(a, "e", "E", -1)函数将字符串a中所有的"e"替换为"E"。最后一个参数-1表示替换所有的匹配项。
  7. 分割字符串strings.Split("1[2[3", "[")函数根据"["符号将字符串"1[2[3"分割为多个子串,返回一个包含所有子串的切片。
  8. 获取字符串长度len(a)函数返回字符串a的长度,即字符数。Go中的len()函数计算的是字符串中的字节数,而非Unicode字符数。对于ASCII字符,字节数和字符数是相同的。但对于非ASCII字符,如中文字符,一个字符由三个字节组成。

字符串格式化

package main

import "fmt"

type point struct {
	x, y int
}

func main() {
	p := point{1, 2}
	fmt.Printf("p=%v\n", p)
	fmt.Printf("p=% v\n", p)
	fmt.Printf("p=%#v\n", p)
	f := 3.14159
	fmt.Printf("f=%.2f\n", f)
}
  1. fmt.Printf("p=%v\n", p):这里的%v是一个通用的值表示,对于结构体,它会输出结构体的字段值,但不会输出字段名。所以这行代码会输出:p={1 2}
  2. fmt.Printf("p=% v\n", p)% v也是一个通用的值表示,不过对于结构体,它会同时输出字段名和字段值。所以这行代码会输出:p={x:1 y:2}
  3. fmt.Printf("p=%#v\n", p)%#v是一个更详细的值表示,它会输出Go语法格式的值。对于结构体,它会输出结构体的类型名、字段名和字段值。所以这行代码会输出:p=main.point{x:1, y:2}
  4. fmt.Printf("f=%.2f\n", f):这里的%.2f表示以浮点数形式输出,且小数部分保留两位。所以这行代码会输出:f=3.14

可以使用%v打印任意类型变量,使用% v,%#v打印更详细的结果

JSON处理

package main

import (
	"encoding/json"
	"fmt"
)

type userInfo struct {
	Name  string
	Age   int `json:"age"`
	Hobby []string
}

func main() {
	a := userInfo{"wang", 18, []string{"Golang", "Java"}}
	buf, err := json.Marshal(a)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(buf))
	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)
}
  1. 定义数据结构userInfo结构体被定义,包含三个字段:Name、Age和Hobby。Age字段有一个结构体标签,它会影响JSON序列化和反序列化时该字段的名称。
  2. 初始化数据:创建了一个userInfo类型的变量a,并初始化为一个具有特定名字、年龄和爱好的用户。
  3. 序列化数据:使用json.Marshal()函数将a实例序列化为JSON格式的字节流。如果序列化过程中出现错误,程序会panic并终止运行。之后,使用json.MarshalIndent()函数将a实例序列化为带有缩进的JSON格式的字节流,使其更易读。
  4. 反序列化数据:使用json.Unmarshal()函数将序列化的JSON数据反序列化为userInfo类型的实例。如果反序列化过程中出现错误,程序会panic并终止运行。

使用json.Marshal(),json.MarshalIndent()序列化实例,后者可以对其进行格式化。使用json.Unmarshal()进行反序列化。

在结构体字段后面添加标签设置该字段序列化和反序列化时使用的key

`json:"age"`

运行结果:

{"Name":"wang","age":18,"Hobby":["Golang","Java"]} { "Name": "wang", "age": 18, "Hobby": [ "Golang", "Java" ] } main.userInfo{Name:"wang", Age:18, Hobby:[]string{"Golang", "Java"}}

时间处理

package main

import (
	"fmt"
	"time"
)

func main() {
	now := time.Now()
	fmt.Println(now)
	date := time.Date(2023, 5, 14, 12, 2, 0, 0, time.UTC)
	date2 := time.Date(2023, 5, 15, 12, 1, 0, 0, time.UTC)
	fmt.Println(date.Format("2006-01-02 15:04:05"))
	sub := date2.Sub(date)
	fmt.Println(sub)
	fmt.Println(sub.Hours(), sub.Seconds())
	parse, err := time.Parse("2006-01-02 15:04:05", "2023-02-03 11:23:33")
	if err != nil {
		panic(err)
	}
	fmt.Println(parse == date2)
	//时间戳
	fmt.Println(now.Unix())
}
  1. 获取当前时间:使用time.Now()函数获取当前时间,并将其存储在变量now中,然后打印出来。
  2. 创建特定时间:使用time.Date()函数创建了两个特定的时间点datedate2。这两个函数的参数分别是年份、月份、日期、小时、分钟、秒钟、纳秒以及时区。在这个例子中,时区被设置为UTC。
  3. 格式化时间:使用date.Format()函数将date变量的时间格式化为"2006-01-02 15:04:05"的形式。这种时间格式化的模板在Go中是固定的。
  4. 计算时间差:使用date2.Sub(date)计算date2date之间的时间差,返回的是一个time.Duration类型的值,并打印出来。然后通过sub.Hours()sub.Seconds()将时间差转换为小时和秒的形式。
  5. 解析时间字符串:使用time.Parse()函数将字符串"2023-02-03 11:23:33"解析为时间。解析的格式与之前的格式化相同,也是"2006-01-02 15:04:05"。然后将解析得到的时间与date2比较,检查两者是否相等。
  6. 获取时间戳:使用now.Unix()函数将now变量的时间转换为Unix时间戳,即从1970年1月1日开始计算的秒数,并打印出来。

数字解析

package main

import (
	"fmt"
	"strconv"
)

func main() {
	float, _ := strconv.ParseFloat("1.234", 64)
	fmt.Println(float)
	//设置进制
	i, _ := strconv.ParseInt("1321", 10, 64)
	fmt.Println(i)
	//0代表自动推断进制
	i2, _ := strconv.ParseInt("0x99A1", 0, 64)
	fmt.Println(i2)
	atoi, _ := strconv.Atoi("13121342")
	fmt.Println(atoi)
}
  1. 解析浮点数strconv.ParseFloat("1.234", 64)函数尝试将字符串"1.234"解析为浮点数。第二个参数64表示解析的浮点数为64位(float64)。解析成功后,该函数返回解析得到的浮点数和一个nil错误。
  2. 解析十进制整数strconv.ParseInt("1321", 10, 64)函数尝试将字符串"1321"解析为整数。这里的第二个参数10表示解析的是十进制的数。第三个参数64表示解析的整数为64位(也就是int64)。
  3. 自动推断进制解析整数strconv.ParseInt("0x99A1", 0, 64)函数尝试将字符串"0x99A1"解析为整数。这里的第二个参数0表示函数会根据字符串的前缀("0x"或"0")自动推断要解析的进制:如果是"0x"或"0X"开头,则解析为十六进制;如果是"0"开头,则解析为八进制;否则解析为十进制。
  4. 解析整数strconv.Atoi("13121342")函数尝试将字符串"13121342"解析为整数。这是strconv.ParseInt(s, 10, 0)的简写,直接解析为int类型。这个函数通常在你不关心具体的int位数(32位还是64位)时使用。

进程信息

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println(os.Args)          // 打印命令行参数
	fmt.Println(os.Getenv("PATH")) // 打印PATH环境变量
	fmt.Println(os.Getppid())      // 打印父进程ID
	fmt.Println(os.Getpid())       // 打印当前进程ID
}
  1. os.Args:打印出程序启动时的所有命令行参数。
  2. os.Getenv("PATH"):打印出PATH环境变量的值,它是由冒号分隔的目录列表,用于搜索可执行文件。
  3. os.Getppid():打印出父进程的ID。对于由shell直接启动的程序,其父进程通常是shell。
  4. os.Getpid():打印出当前进程的ID。每个运行的程序都有一个唯一的进程ID。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhbjeb
系列文章
更多 icon
同类精品
更多 icon
继续加载