Go语言基础 | 青训营

83 阅读2分钟

声明变量

变量、类型和关键字变量的类型在变量名的后面

  • 用 = 声明
var a i n t
var b bool
a = 15
b = false
  • 用:= 声明
a := 15
b := false
  • 多个 var 声明可以成组;const 和 import 同样允许这么做。
var (
	x int
	b bool
)

有相同类型的多个变量同样可以在一行内完成声明:var x, y int 让 x 和 y 都是 int类型变量。 同样可以使用 平行赋值:a, b := 20, 16 特殊的变量名是 _(下划线)。任何赋给它的值都被丢弃

常量在 Go 中,也就是 constant。 可以使用 iota  生成枚举值。意思是 “不是最小在 Go 中字符串是不可变的。从 C 来的用户,下面的情况在 Go 中是非法的。 var s string = "hello" s[0] = 'c' <- 修改第一个字符为’c’,这会报错

  • Go 原生支持复数 使用复数的一个例子:
var c complex64 = 5+5i
fmt.Printf("Value is: %v", c)

  Printf() 的 %v 参数含义是 “用默认格式打印这个值”,将会打印:(5+5i)

字符串

常用函数

a := "hello"  
fmt.Println(strings.Contains(a, "ll"))                // true  
fmt.Println(strings.Count(a, "l"))                    // 2  
fmt.Println(strings.HasPrefix(a, "he"))               // true  
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  
fmt.Println(len(a))                                   // 5  
b := "你好"  
fmt.Println(len(b)) // 6

标准格式

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

%v:默认格式化

这种格式化方法使用默认的打印格式来输出变量 p 的值。它会根据变量的类型选择合适的格式进行打印。对于结构体类型,将按照字段顺序逐个打印输出。对于数字类型,将直接输出数字。这是一种通用的格式化方式。

%+v:包含字段名的格式化

这种格式化方法在 %v 的基础上,除了打印变量的值外,还会打印结构体的字段名。这样可以更清晰地显示出结构体的字段及其对应的值。

%#v:Go 语法表示的格式化

这种格式化方法在 %v 的基础上,以一种类似于 Go 语法的格式输出变量的值。它会打印出结构体的完整类型以及每个字段的名称和值。这对于了解结构体的结构和内容非常有帮助。

多行字符串

s := "Starting part"
+"Ending part"

另一种方式是使用反引号`作为原始字符串符号:

s := `Starting part
Ending part`

控制结构

这里没有do或者while循环,只有for。 语法有所不同(同C相比),没有圆括号。 Go有goto语句标签名是大小写敏感的。 如果想在for中执行多个变量,应当使用平行赋值。

break j <-现在终止的是j循环,而不是i的

swich-case结构默认跑完一个分支就不继续

Silce(切片)

可更改长度的数组,一般写作[]T,T是元素类型 如果你需要测试一个slice是否是空的,使用len(s) == 0来判断,而不应该用s == nil来判断

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  
  
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]

Map(哈希表)

一般定义map的方法:

m := make(map[string]int)  
m["one"] = 1  
m["two"] = 2  

//等价写法
m := map[string]int{
    "one": 1,
    "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")  
  
m2 := map[string]int{"one": 1, "two": 2}  
var m3 = map[string]int{"one": 1, "two": 2}  
fmt.Println(m2, m3)

Range

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  

每次迭代,range 返回当前元素在切片中的索引和对应的值。

在循环遍历映射(map)时使用 range

m := map[string]string{"a": "A", "b": "B"}  
for k, v := range m {  
   fmt.Println(k, v) // b B; a A  
}  

每次迭代,range 返回映射中的键和对应的值。我们可以通过 kv 分别获取键和值,并将它们打印出来。由于映射是无序的,所以迭代的顺序可能不同。

在循环遍历映射时仅使用键或值:

for k := range m {  
   fmt.Println("key", k) // key a; key b
}
for _, v := range m {  
   fmt.Println("value", v) // value A;value B  
}

在第一个循环中,我们只使用 range 返回的键,并将它们打印出来。在第二个循环中,我们使用 _ 忽略了 range 返回的键,只使用值并将其打印出来。因为我们只关心循环的键或值,而不需要使用索引或其他变量,所以我们使用 _ 来忽略它们。

结构体

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, "haha"))   // false  
   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  
}

将函数改为成员方法:

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  
}

值接收者方法和指针接收者方法的区别:

  1. 修改原始实例:值接收者方法在方法内部对接收者进行操作时,操作的是接收者的副本,不会对原始实例产生影响。而指针接收者方法通过指针操作原始实例,可以修改原始实例的状态。
  2. 参数传递:值接收者方法将接收者作为值传递给方法,方法使用的是接收者的副本。而指针接收者方法将接收者作为指针传递给方法,方法使用的是接收者的地址,可以直接访问和修改接收者的字段。
  3. 性能开销:值接收者方法在调用时需要进行值的复制,可能会产生一定的性能开销。而指针接收者方法通过指针传递,可以避免复制大型结构体的开销。
  4. 方法调用:对于值类型的实例,可以同时调用值接收者方法和指针接收者方法;而对于指针类型的实例,只能调用指针接收者方法,不能调用值接收者方法。 选择使用值接收者方法还是指针接收者方法取决于具体需求。如果需要修改原始实例的状态,或者实例较大并希望避免复制开销,可以使用指针接收者方法。如果只是对实例进行简单操作,不需要修改原始实例的状态,可以使用值接收者方法。

异常

使用返回err来处理 在函数返回值类型里面加err error

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")  //有错误返回nil
}

func main() {  
   u, err := findUser([]user{{"wang", "1024"}}, "wang")  
   if err != nil {  //有错误返回错误值
      fmt.Println(err)  
      return  
   }  
   fmt.Println(u.name) // wang
}

JSON

针对已有的结构体,只要将每个字段的首字母大写

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

就可以用json.Marshal()数列化,可以再用json.Unmarshal反数列化

时间处理

now := time.Now()  
fmt.Println(now) // 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933  
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  
diff := t2.Sub(t)  
fmt.Println(diff)                           // 1h5m0s  
fmt.Println(diff.Minutes(), diff.Seconds()) // 65 3900  
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  
fmt.Println(now.Unix()) // 1648738080 时间戳

数值解析

f, _ := strconv.ParseFloat("1.234", 64)  //(要解析的字符串, 浮点数精度位数)
fmt.Println(f) // 1.234  
  
n, _ := strconv.ParseInt("111", 10, 64)  //(要解析的字符串, 基数(n进制), 整数位数)
fmt.Println(n) // 111  
  
n, _ = strconv.ParseInt("0x1000", 0, 64) 
fmt.Println(n) // 4096  
  
n2, _ := strconv.Atoi("123")  //将字符串解析为 int 类型的整数
fmt.Println(n2) // 123  
  
n2, err := strconv.Atoi("AAA")  
fmt.Println(n2, err) // 0 strconv.Atoi: parsing "AAA": invalid syntax

当基数为 0 时,函数会根据字符串的前缀来判断进制方式。如果字符串以 "0x" 开头,表示是一个十六进制数;如果字符串以 "0" 开头,表示是一个八进制数;其他情况下,默认采用十进制。

进程信息

fmt.Println(os.Args)           // [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d]  
fmt.Println(os.Getenv("PATH")) // /usr/local/go/bin...  
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)) // 127.0.0.1       localhost

GPT说:

这段代码展示了在 Go 编程语言中使用 osexec 包的一些功能。

fmt.Println(os.Args)os.Args 是一个字符串切片,保存了程序运行时传递的命令行参数。通过 fmt.Println 打印出 os.Args 的值,可以显示程序运行时传递的参数列表。在这个例子中,输出是 [/var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main a b c d],其中 /var/folders/8p/n34xxfnx38dg8bv_x8l62t_m0000gn/T/go-build3406981276/b001/exe/main 是可执行文件的路径,a b c d 是传递给程序的参数。

fmt.Println(os.Getenv("PATH"))os.Getenv 函数用于获取环境变量的值。在这个例子中,通过传递 "PATH" 作为参数,获取系统环境变量 "PATH" 的值,并将其打印出来。输出的结果是 "/usr/local/go/bin..."

fmt.Println(os.Setenv("AA", "BB"))os.Setenv 函数用于设置环境变量的值。在这个例子中,将环境变量 "AA" 的值设置为 "BB",并将设置结果打印出来。输出的结果是 true,表示设置环境变量成功。

exec.CommandCombinedOutput:这两个函数用于执行外部命令。exec.Command 用于创建一个执行指定命令的 *exec.Cmd 结构体,而 CombinedOutput 方法用于执行该命令并返回其标准输出和标准错误的组合结果。 这段代码通过执行命令 grep 127.0.0.1 /etc/hosts 来在 /etc/hosts 文件中查找包含 127.0.0.1 的行,并将结果保存在变量 buf 中。如果执行命令时出现错误,使用 panic 函数抛出该错误。否则,将 buf 的内容转换为字符串并打印出来。在这个例子中,输出的结果是 127.0.0.1 localhost,表示在 /etc/hosts 文件中找到了包含 127.0.0.1 的行。