Go语言上手-基础语言

一、变量

在Golang中,定义变量使用:=或者var i

Golang是强类型语言,必须声明变量类型(或初始化过程中获得类型)。

var a = "initial" //初始化获得类型

var b, c int = 1, 2 //提前声明类型

var d = true //初始化获得类型
 
var e float64 //提前声明类型

f := float32(e) //初始化获得类型

二、条件语句

在Golang中,if else的 使用类似于C++、Java等,但不需要小括号包裹条件。

if 7%2 == 0 {
   fmt.Println("7 is even")
} else {
   fmt.Println("7 is odd")
}

但在Golang中,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")
}

三、循环语句

Golang中只有for循环

1、无限制条件

for {
   fmt.Println("loop")
   break
}

这种默认为恒成立

2、有限制条件

for i <= 3 {
   fmt.Println(i)
   i = i + 1
}

这种类似于C++、Java中的while循环

3、有前置语句,循环语句和循环语句

for j := 7; j < 9; j++ {
   fmt.Println(j)
}

这种类似于C++、Java中的fori循环

4、continue、break

continue和break的用法与C++、Java相同

for n := 0; n < 5; n++ {
   if n%2 == 0 {
      continue
   }
   fmt.Println(n)
}

四、分支结构

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")
}

在Golang中,switch执行完一个case后默认会跳出switch,不会继续执行。变量名也不用添加括号。

也可以使用任意的数据类型,例如字符串等等。

五、数组

数组是一个可以存放多个相同类型数据的结构。

var a [5]int
a[4] = 100

可以很轻松的使用索引来使用数组中的值

fmt.Println("len:", len(a))

可以通过len()来获取数组的长度。

在golang中,数组的长度是不可变的。

六、切片

切片是一种可变长度的数组。

创建

可以使用make进行创建

s := make([]string, 3)

这里就创建了一个string类型的切片,初始容量为3

使用

切片的使用类似于数组

s[0] = "a"
s[1] = "b"
s[2] = "c"

追加元素

s = append(s, "d")
s = append(s, "e", "f")

追加之后需要赋值给原来的切片

拷贝

copy(c, s)

切片可以通过copy进行拷贝复制

截取

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]

切片可以通过:索引的方式来获取其中的值(通常也是删除切片中元素的方式)

七、map

存储key-value类型的数据。在golang中,map是完全无序的,输出是纯随机的。

创建

通过make进行创建

m := make(map[string]int)

使用

m["one"] = 1
m["two"] = 2

获取map里面的值

r, ok := m["unknow"]

这里的r是取出来的值,ok是是否取出

删除

使用delete进行删除

delete(m, "one")

八、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
   }
}

range遍历会返回两个值,第一个是索引,第二个是遍历的值

九、函数

golang中函数是可以返回多个值的,通常情况下,第一个是用的值,第二个是是否成功或者说存在。

定义方式

func 函数名(参数) 返回值类型 {
    return 返回值
}
func add(a int, b int) int {
   return a + b
}

func add2(a, b int) int {
   return a + b
}

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

十、指针

在golang中,指针的操作并不如C、C++中那么灵活,一般只是用来对传入的参数进行修改。

func add2(n int) {
   n += 2
}

func add2ptr(n *int) {
   *n += 2
}

例如这里,上面这种方式是无法对n进行修改的,下面这种方式通过指针就可以实现。

十一、结构体

结构体是带类型的字段的集合

type user struct {
   name     string
   password string
}

创建一个结构体变量

可以通过结构体名来创建

a := user{name: "wang", password: "1024"}
b := user{"wang", "1024"}
c := user{name: "wang"}

第一种通过键值对的方式,值一一对应

第二种按照结构体中的顺序对应

第三种缺少的如果结构体中有默认就为默认,没有则为空

使用

结构体中的数据,可以通过.来进行使用

d.name = "wang"
d.password = "1024"

结构体方法

在golang中,可以为结构体定义一些方法

类似于在面向对象中的成员方法,这里可以使用.对方法进行调用

func checkPassword(u user, password string) bool {
   return u.password == password
}

func checkPassword2(u *user, password string) bool {
   return u.password == password
}

十二、错误处理

在golang中,通常使用函数的返回值来返回错误信息,然后可以通过if else来轻松对异常进行处理

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)
}
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")
}

这里就利用errors.New创建了一个错误

十三、string操作

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

Contains

判断字符串中是否有对应的子字符串

Count

返回字符串中对应字串的数量

HasPrefix

从前往后判断字符串中是否有对应的子字符串

HasSuffix

从后往前判断字符串中是否有对应的子字符串

Index

查找某个字符串的位置(返回首个字符出现的位置)

Join

连接多个字符串,可以接收string类型的切片和string

Repeat

重复一个字符串多次,并拼接

Split

通过后面的字符串去切割字符串

ToLower

将字符串小写

ToUpper

将字符串大写

字符串格式化

fmt.Println(s, n)

多个变量之间空格,打印完换行

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}

%v使用不同的前缀,可以获得不同的输出结果,根据需要选择

十四、json处理

在golang中,将结构体转换为json格式的字符串,要保证结构体中的每个字段的首字母大写(公开)

通过json.Marshall进行序列化,然后通过string()将其转换为字符串

可以使用json.Unmarshall将其反序列化到一个结构体变量中

当我们需要一个不同名字的json时,可以使用json:"名字"将其转换为对应的名字

a := userInfo{Name: "wang", Age: 18, Hobby: []string{"Golang", "TypeScript"}}
buf, err := json.Marshal(a)
err = json.Unmarshal(buf, &b)
if err != nil {
   panic(err)
}

十五、时间处理

time.Now()

获取当前时间

time.Date()

构造一个带时区的时间

可以通过.来获取时间点的一些信息,如年月日等。

Sub

对两个时间进行相减

格式化

运用一个具体时间

Format或者Parse进行

Unix

获取时间戳

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

十六、数字解析

在strconv包下面,可以使用这个包进行对应的转换

f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f) // 1.234

n, _ := strconv.ParseInt("111", 10, 64)
fmt.Println(n) // 111

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

第一个参数是对应字符串,第二个参数是进制(0为自动推断),也可以用Atoi来进行一个快速转换

字符串中的如果不合法就会返回错误err

十七、进程信息

// go run example/20-env/main.go a b c d
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

可以获取一些进程的相关参数

可以使用Getenv获取环境变量,可以通过Setenv设置环境变量。启动子进程并且输入输出exec.Command。

十八、猜谜游戏实战

package main

import (
   "fmt"
   "math/rand"
   "time"
)

func main() {
   maxNum := 100
   rand.Seed(time.Now().Unix())
   randNum := rand.Intn(maxNum)

   fmt.Println("Please input your guess")
   for {
      var guessNum int
      _, err := fmt.Scanf("%v\n", &guessNum)
      if err != nil {
         fmt.Println("Invalid input.Please enter an integer value")
         continue
      }
      if guessNum > randNum {
         fmt.Println("Your guess is bigger than the secret number,please input another")
      } else if guessNum < randNum {
         fmt.Println("Your guess is smaller than the secret number,please input another")
      } else {
         fmt.Println("Wow,Your guess is right")
         break
      }
   }
}

十九、在线词典

package main

import (
   "encoding/json"
   "fmt"
   "io/ioutil"
   "log"
   "net/http"
   "strings"
)

type DictResponse struct {
   Rc   int `json:"rc"`
   Wiki struct {
      KnownInLaguages int `json:"known_in_laguages"`
      Description     struct {
         Source string      `json:"source"`
         Target interface{} `json:"target"`
      } `json:"description"`
      ID   string `json:"id"`
      Item struct {
         Source string `json:"source"`
         Target string `json:"target"`
      } `json:"item"`
      ImageURL  string `json:"image_url"`
      IsSubject string `json:"is_subject"`
      Sitelink  string `json:"sitelink"`
   } `json:"wiki"`
   Dictionary struct {
      Prons struct {
         EnUs string `json:"en-us"`
         En   string `json:"en"`
      } `json:"prons"`
      Explanations []string      `json:"explanations"`
      Synonym      []string      `json:"synonym"`
      Antonym      []string      `json:"antonym"`
      WqxExample   [][]string    `json:"wqx_example"`
      Entry        string        `json:"entry"`
      Type         string        `json:"type"`
      Related      []interface{} `json:"related"`
      Source       string        `json:"source"`
   } `json:"dictionary"`
}

func query(word string) {
   client := &http.Client{}
   var data = strings.NewReader(`{"trans_type":"en2zh","source":"` + word + `"}`)
   req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
   if err != nil {
      log.Fatal(err)
   }
   req.Header.Set("Accept", "application/json, text/plain, */*")
   req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
   req.Header.Set("Connection", "keep-alive")
   req.Header.Set("Content-Type", "application/json;charset=UTF-8")
   req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
   req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
   req.Header.Set("Sec-Fetch-Dest", "empty")
   req.Header.Set("Sec-Fetch-Mode", "cors")
   req.Header.Set("Sec-Fetch-Site", "cross-site")
   req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36")
   req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
   req.Header.Set("app-name", "xy")
   req.Header.Set("os-type", "web")
   req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"`)
   req.Header.Set("sec-ch-ua-mobile", "?0")
   req.Header.Set("sec-ch-ua-platform", `"Windows"`)
   resp, err := client.Do(req)
   if err != nil {
      log.Fatal(err)
   }

   defer resp.Body.Close()
   bodyText, err := ioutil.ReadAll(resp.Body)
   if err != nil {
      log.Fatal(err)
   }
   if resp.StatusCode != 200 {
      log.Fatal("bad StatusCode", resp.StatusCode, "body", string(bodyText))
   }
   var dictResponse DictResponse
   json.Unmarshal(bodyText, &dictResponse)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println("UK", dictResponse.Dictionary.Prons.En, "US", dictResponse.Dictionary.Prons.EnUs)
   for _, item := range dictResponse.Dictionary.Explanations {
      fmt.Println(item)
   }
}

func main() {
   var s string
   fmt.Scan(&s)
   query(s)
}

二十、代理服务器

package main

import (
   "bufio"
   "context"
   "encoding/binary"
   "errors"
   "fmt"
   "io"
   "log"
   "net"
)

const socks5Ver = 0x05
const cmdBind = 0x01
const atypIPV4 = 0x01
const atypeHOST = 0x03
const atypeIPV6 = 0x04

func main() {
   server, err := net.Listen("tcp", "127.0.0.1:1081")
   if err != nil {
      panic(err)
   }
   for {
      client, err := server.Accept()
      if err != nil {
         log.Printf("Accept failed %v", err)
         continue
      }
      go process(client)
   }
}

func process(conn net.Conn) {
   defer conn.Close()
   reader := bufio.NewReader(conn)
   err := auth(reader, conn)
   if err != nil {
      log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
      return
   }
   err = connect(reader, conn)
   if err != nil {
      log.Printf("client %v auth failed:%v", conn.RemoteAddr(), err)
      return
   }
}

func auth(reader *bufio.Reader, conn net.Conn) (err error) {
   // +----+----------+----------+
   // |VER | NMETHODS | METHODS  |
   // +----+----------+----------+
   // | 1  |    1     | 1 to 255 |
   // +----+----------+----------+
   // VER: 协议版本,socks5为0x05
   // NMETHODS: 支持认证的方法数量
   // METHODS: 对应NMETHODS,NMETHODS的值为多少,METHODS就有多少个字节。RFC预定义了一些值的含义,内容如下:
   // X’00’ NO AUTHENTICATION REQUIRED
   // X’02’ USERNAME/PASSWORD

   ver, err := reader.ReadByte()
   if err != nil {
      return fmt.Errorf("read ver failed:%w", err)
   }
   if ver != socks5Ver {
      return fmt.Errorf("not supported ver:%v", ver)
   }
   methodSize, err := reader.ReadByte()
   if err != nil {
      return fmt.Errorf("read methodSize failed:%w", err)
   }
   method := make([]byte, methodSize)
   _, err = io.ReadFull(reader, method)
   if err != nil {
      return fmt.Errorf("read method failed:%w", err)
   }

   // +----+--------+
   // |VER | METHOD |
   // +----+--------+
   // | 1  |   1    |
   // +----+--------+
   _, err = conn.Write([]byte{socks5Ver, 0x00})
   if err != nil {
      return fmt.Errorf("write failed:%w", err)
   }
   return nil
}

func connect(reader *bufio.Reader, conn net.Conn) (err error) {
   // +----+-----+-------+------+----------+----------+
   // |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
   // +----+-----+-------+------+----------+----------+
   // | 1  |  1  | X'00' |  1   | Variable |    2     |
   // +----+-----+-------+------+----------+----------+
   // VER 版本号,socks5的值为0x05
   // CMD 0x01表示CONNECT请求
   // RSV 保留字段,值为0x00
   // ATYP 目标地址类型,DST.ADDR的数据对应这个字段的类型。
   //   0x01表示IPv4地址,DST.ADDR为4个字节
   //   0x03表示域名,DST.ADDR是一个可变长度的域名
   // DST.ADDR 一个可变长度的值
   // DST.PORT 目标端口,固定2个字节

   buf := make([]byte, 4)
   _, err = io.ReadFull(reader, buf)
   if err != nil {
      return fmt.Errorf("read header failed:%w", err)
   }
   ver, cmd, atyp := buf[0], buf[1], buf[3]
   if ver != socks5Ver {
      return fmt.Errorf("not supported ver:%v", ver)
   }
   if cmd != cmdBind {
      return fmt.Errorf("not supported cmd:%v", ver)
   }
   addr := ""
   switch atyp {
   case atypIPV4:
      _, err = io.ReadFull(reader, buf)
      if err != nil {
         return fmt.Errorf("read atyp failed:%w", err)
      }
      addr = fmt.Sprintf("%d.%d.%d.%d", buf[0], buf[1], buf[2], buf[3])
   case atypeHOST:
      hostSize, err := reader.ReadByte()
      if err != nil {
         return fmt.Errorf("read hostSize failed:%w", err)
      }
      host := make([]byte, hostSize)
      _, err = io.ReadFull(reader, host)
      if err != nil {
         return fmt.Errorf("read host failed:%w", err)
      }
      addr = string(host)
   case atypeIPV6:
      return errors.New("IPv6: no supported yet")
   default:
      return errors.New("invalid atyp")
   }
   _, err = io.ReadFull(reader, buf[:2])
   if err != nil {
      return fmt.Errorf("read port failed:%w", err)
   }
   port := binary.BigEndian.Uint16(buf[:2])

   dest, err := net.Dial("tcp", fmt.Sprintf("%v:%v", addr, port))
   if err != nil {
      return fmt.Errorf("dial dst failed:%w", err)
   }
   defer dest.Close()
   log.Println("dial", addr, port)

   // +----+-----+-------+------+----------+----------+
   // |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
   // +----+-----+-------+------+----------+----------+
   // | 1  |  1  | X'00' |  1   | Variable |    2     |
   // +----+-----+-------+------+----------+----------+
   // VER socks版本,这里为0x05
   // REP Relay field,内容取值如下 X’00’ succeeded
   // RSV 保留字段
   // ATYPE 地址类型
   // BND.ADDR 服务绑定的地址
   // BND.PORT 服务绑定的端口DST.PORT
   _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})
   if err != nil {
      return fmt.Errorf("write failed: %w", err)
   }
   ctx, cancel := context.WithCancel(context.Background())
   defer cancel()

   go func() {
      _, _ = io.Copy(dest, reader)
      cancel()
   }()
   go func() {
      _, _ = io.Copy(conn, dest)
      cancel()
   }()

   <-ctx.Done()
   return nil
}

你可能感兴趣的:(go,golang,go)