go基础练习-中

 

一些练习题,简单回顾一下六到八节的内容

排序、

一起学习:smiley:

排序

package main

import (
    "fmt"
    "sort"
)

func main() {

    strs := []string{"c", "a", "b"}
    sort.Strings(strs)//数组排序
    fmt.Println("Strings:", strs)

    ints := []int{7, 2, 4}
    sort.Ints(ints)//数组排序
    fmt.Println("Ints:   ", ints)

    //是否排好序
    s := sort.IntsAreSorted(ints)
    fmt.Println("Sorted: ", s)
}
Strings: [a b c]
Ints:    [2 4 7]
Sorted:  true

使用函数自定义排序

有时候,我们可能想根据自然顺序以外的方式来对集合进行排序。

例如,假设我们要按字符串的长度而不是按字母顺序对它们进行排序。

这儿有一个在 Go 中自定义排序的示例。

package main

import (
    "fmt"
    "sort"
)
// byLength 类型,它只是内建类型 []string 的别名。
type byLength []string//这里不想C语言是起别名,这里是定义了新的类型

/*
ort.Interface 接口的 Len、Less 和 Swap 方法
*/
func (s byLength) Len() int {
    return len(s)
}
func (s byLength) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
//Less 将控制实际的自定义排序逻辑。
func (s byLength) Less(i, j int) bool {
    return len(s[i]) < len(s[j])
}

func main() {
    fruits := []string{"peach", "banana", "kiwi"}
    //将切片 fruits 强转为 byLength 类型的切片
    sort.Sort(byLength(fruits))
    fmt.Println(fruits)
}
[kiwi peach banana]

别名与新类型

type MyInt1 int   //新的类型
type MyInt2 = int //别名

Panic

运行程序将会导致 panic: 输出一个错误消息和协程追踪信息,并以非零的状态退出程序。

注意,与某些使用 exception 处理错误的语言不同, 在 Go 中,通常会尽可能的使用返回值来标示错误

package main

import "os"

func main() {

    panic("a problem")

    _, err := os.Create("/tmp/file")
    if err != nil {
        panic(err)
    }
}
panic: a problem
goroutine 1 [running]:
main.main()
    /.../panic.go:12 +0x47

Defer

package main

import (
    "fmt"
    "os"
)

func main() {
    f := createFile("/tmp/defer.txt")
    defer closeFile(f)
    writeFile(f)
}

func createFile(p string) *os.File {
    fmt.Println("creating")
    f, err := os.Create(p)
    if err != nil {
        panic(err)
    }
    return f
}

func writeFile(f *os.File) {
    fmt.Println("writing")
    fmt.Fprintln(f, "data")

}

func closeFile(f *os.File) {
    fmt.Println("closing")
    err := f.Close()

    if err != nil {
        fmt.Fprintf(os.Stderr, "error: %v\n", err)//err输出,最后打印
        os.Exit(1)//退出程序
    }
}
creating
writing
closing

Recover

必须在 defer 函数中调用 recover。 当跳出引发 panic 的函数时,defer 会被激活, 其中的 recover 会捕获 panic

package main

import "fmt"

func mayPanic() {
    panic("a problem")
}

func main() {

    defer func() {
        //recover 的返回值是在调用 panic 时抛出的错误。
        if r := recover(); r != nil {

            fmt.Println("Recovered. Error:\n", r)
        }
    }()

    mayPanic()

    //这行代码不会执行,因为 mayPanic 函数会调用 panic。 main 程序的执行在 panic 点停止,并在继续处理完 defer 后结束。
    fmt.Println("After mayPanic()")
}
Recovered. Error:
 a problem

组合函数

package main

import (
	"fmt"
	"strings"
)

func Index(vs []string, t string) int {
	for i, v := range vs {
		if v == t {
			return i+1
		}
	}
	return -1
}

func Include(vs []string, t string) bool {
	return Index(vs, t) >= 0
}

func Any(vs []string, f func(string) bool) bool {
	for _, v := range vs {
		if f(v) {
			return true
		}
	}
	return false
}

func All(vs []string, f func(string) bool) bool {
	for _, v := range vs {
		if !f(v) {
			return false
		}
	}
	return true
}

func Filter(vs []string, f func(string) bool) []string {
	//vsf := make([]string, 0)
	vsf := []string{}
	for _, v := range vs {
		if f(v) {
			vsf = append(vsf, v)
		}
	}
	return vsf
}

func Map(vs []string, f func(string) string) []string {
	vsm := make([]string, len(vs))
	for i, v := range vs {
		vsm[i] = f(v)
	}
	return vsm
}

func main() {

	var strs = []string{"peach", "apple", "pear", "plum"}

	fmt.Println(Index(strs, "pear"))

	fmt.Println(Include(strs, "grape"))

	//判断字符串数组中是否含有首字符是否为p的字符串
	fmt.Println(Any(strs, func(v string) bool {
		return strings.HasPrefix(v, "p")
	}))

	//判断字符串数组中是否都含有首字符是否为p的字符串
	fmt.Println(All(strs, func(v string) bool {
		return strings.HasPrefix(v, "p")
	}))

	fmt.Println(Filter(strs, func(v string) bool {
		return strings.Contains(v, "e")
	}))

	fmt.Println(Map(strs, strings.ToUpper))

}
3
false
true
false
[peach apple pear]
[PEACH APPLE PEAR PLUM]

字符串函数

package main

import (
    "fmt"
    s "strings"
)

var p = fmt.Println

func main() {

    p("Contains:  ", s.Contains("test", "es"))
    p("Count:     ", s.Count("test", "t"))
    p("HasPrefix: ", s.HasPrefix("test", "te"))
    p("HasSuffix: ", s.HasSuffix("test", "st"))
    p("Index:     ", s.Index("test", "e"))
    p("Join:      ", s.Join([]string{"a", "b"}, "-"))
    p("Repeat:    ", s.Repeat("a", 5))
    p("Replace:   ", s.Replace("foo", "o", "0", -1))
    p("Replace:   ", s.Replace("foo", "o", "0", 1))
    p("Split:     ", s.Split("a-b-c-d-e", "-"))
    p("ToLower:   ", s.ToLower("TEST"))
    p("ToUpper:   ", s.ToUpper("test"))
    p()

    p("Len: ", len("hello"))
    p("Char:", "hello"[1])
}
Contains:   true
Count:      2
HasPrefix:  true
HasSuffix:  true
Index:      1
Join:       a-b
Repeat:     aaaaa
Replace:    f00
Replace:    f0o
Split:      [a b c d e]
ToLower:    test
ToUpper:    TEST

Len:  5
Char: 101

字符串格式化

package main

import (
    "fmt"
    "os"
)

type point struct {
    x, y int
}

func main() {

    p := point{1, 2}
    fmt.Printf("struct1: %v\n", p)

    fmt.Printf("struct2: %+v\n", p)

    fmt.Printf("struct3: %#v\n", p)

    fmt.Printf("type: %T\n", p)

    fmt.Printf("bool: %t\n", true)

    fmt.Printf("int: %d\n", 123)

    fmt.Printf("bin: %b\n", 14)

    fmt.Printf("char: %c\n", 33)

    fmt.Printf("hex: %x\n", 456)

    fmt.Printf("float1: %f\n", 78.9)

    fmt.Printf("float2: %e\n", 123400000.0)
    fmt.Printf("float3: %E\n", 123400000.0)

    fmt.Printf("str1: %s\n", "\"string\"")

    fmt.Printf("str2: %q\n", "\"string\"")

    fmt.Printf("str3: %x\n", "hex this")

    fmt.Printf("pointer: %p\n", &p)

    fmt.Printf("width1: |%6d|%6d|\n", 12, 345)

    fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45)

    fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45)

    fmt.Printf("width4: |%6s|%6s|\n", "foo", "b")

    fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b")

    s := fmt.Sprintf("sprintf: a %s", "string")
    fmt.Println(s)

    fmt.Fprintf(os.Stderr, "io: an %s\n", "error")
}
struct1: {-1 2}
struct2: {x:-1 y:2}
struct3: main.point{x:-1, y:2}
type: main.point
bool: true
int: 123
bin: 1110
char: !
hex: 1c8
float1: 78.900000
float2: 1.234000e+08
float3: 1.234000E+08
str1: "string"
str2: "\"string\""
str3: 6865782074686973
pointer: 0xc00000a0c0
width1: |    12|   345|
width2: |  1.20|  3.45|
width3: |1.20  |3.45  |
width4: |   foo|     b|
width5: |foo   |b     |
sprintf: a string
io: an error

正则表达式

后面的一些API没有了解

package main

import (
	"bytes"
	"fmt"
	"regexp"
)

func main() {

	match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
	fmt.Println(match)

	r, _ := regexp.Compile("p([a-z]+)ch")

	fmt.Println(r.MatchString("peach"))

	//查找匹配的字符串。
	fmt.Println(r.FindString("peach punch"))

	//匹配开始和结束位置的索引 idx: [0 5]
	fmt.Println("idx:", r.FindStringIndex("peach punch"))

	//Submatch 返回完全匹配和局部匹配的字符串 [peach ea]
	fmt.Println(r.FindStringSubmatch("peach punch"))

	//[0 5 1 3]
	fmt.Println(r.FindStringSubmatchIndex("peach punch"))

	//非负整数作为第二个参数,来限制匹配次数。负数就是匹配全部
	fmt.Println(r.FindAllString("peach punch pinch", -1))

	fmt.Println("all:", r.FindAllStringSubmatchIndex(
		"peach punch pinch", -1))

	fmt.Println(r.FindAllString("peach punch pinch", 2))

	fmt.Println(r.Match([]byte("peach")))

	r = regexp.MustCompile("p([a-z]+)ch")
	fmt.Println("regexp:", r)

	fmt.Println(r.ReplaceAllString("a peach", "<fruit>"))

	in := []byte("a peach")
	out := r.ReplaceAllFunc(in, bytes.ToUpper)
	fmt.Println(string(out))
}
true
true
peach
idx: [0 5]
[peach ea]
[0 5 1 3]
[peach punch pinch]
all: [[0 5 1 3] [6 11 7 9] [12 17 13 15]]
[peach punch]
true
regexp: p([a-z]+)ch
a <fruit>
a PEACH

JSON

package main

import (
   "encoding/json"
   "fmt"
   "os"
)

type response1 struct {
   Page   int
   Fruits []string
}

type response2 struct {
   Page   int      `json:"page"`
   Fruits []string `json:"fruits"`
}

func main() {

   bolB, _ := json.Marshal(true)
   fmt.Println(string(bolB))

   intB, _ := json.Marshal(1)
   fmt.Println(string(intB))

   fltB, _ := json.Marshal(2.34)
   fmt.Println(string(fltB))

   strB, _ := json.Marshal("gopher")
   //"gopher"
   fmt.Println(string(strB))

   slcD := []string{"apple", "peach", "pear"}
   slcB, _ := json.Marshal(slcD)
   //["apple","peach","pear"]
   fmt.Println(string(slcB))

   mapD := map[string]int{"apple": 5, "lettuce": 7}
   mapB, _ := json.Marshal(mapD)
   //{"apple":5,"lettuce":7}
   fmt.Println(string(mapB))

   res1D := &response1{
      Page:   1,
      Fruits: []string{"apple", "peach", "pear"}}
   res1B, _ := json.Marshal(res1D)
   //{"Page":1,"Fruits":["apple","peach","pear"]}
   fmt.Println(string(res1B))

   res2D := &response2{
      Page:   1,
      Fruits: []string{"apple", "peach", "pear"}}
   res2B, _ := json.Marshal(res2D)
   //{"page":1,"fruits":["apple","peach","pear"]} 这里的小写是struct设定的
   fmt.Println(string(res2B))

   //模拟json转码后的格式
   byt := []byte(`{"num":6.13,"strs":["a","b"]}`)

   //提供一个 JSON 包可以存放解码数据的变量
   var dat map[string]interface{}

   //判断解码是否成功
   if err := json.Unmarshal(byt, &dat); err != nil {
      panic(err)
   }
   //map[num:6.13 strs:[a b]]
   fmt.Println(dat)

   num := dat["num"].(float64)//因为是interface可以断言
   //6.13
   fmt.Println(num)

   strs := dat["strs"].([]interface{})
   str1 := strs[0].(string)
   //a
   fmt.Println(str1)

   //使用案例
   str := `{"page": 1, "fruits": ["apple", "peach"]}`
   //承接JSON格式的结构体
   res := response2{}
   //解码
   json.Unmarshal([]byte(str), &res)
   //{1 [apple peach]}
   fmt.Println(res)
   //apple
   fmt.Println(res.Fruits[0])

   // os.Stdout 一样直接将 JSON 编码流传输到 os.Writer
   enc := json.NewEncoder(os.Stdout)//直接转码到stdout
   d := map[string]int{"apple": 5, "lettuce": 7}
   //转码
   enc.Encode(d)//直接转码到stdout {"apple":5,"lettuce":7}
}

时间

/**
  @Go version: 1.17.6
  @project: TeachCode
  @ide: GoLand
  @file: timeApi.go
  @author: Lido
  @time: 2022-03-03 14:36
  @description: Time
*/
package main

import (
	"fmt"
	"time"
)

func main() {
	p := fmt.Println

	now := time.Now()
	p(now)

	then := time.Date(
		2009, 11, 17, 20, 34, 58, 651387237, time.Local)
	p(then)

	p(then.Year())//2009
	p(then.Month())//11
	p(then.Day())//17
	p(then.Hour())
	p(then.Minute())
	p(then.Second())
	p(then.Nanosecond())
	p(then.Location())

	p(then.Weekday())//Tuesday

	p(then.Before(now))//true
	p(then.After(now))//false
	p(then.Equal(now))//false

	diff := now.Sub(then)//方法 Sub 返回一个 Duration 来表示两个时间点的间隔时间
	p(diff)//时间差 107730h2m13.696547563s

	p(diff.Hours())//以小时表示这段时间 107730.03713792987 
	p(diff.Minutes())
	p(diff.Seconds())
	p(diff.Nanoseconds())

	p(then.Add(diff))//减去一段时间
	p(then.Add(-diff))//增加一段时间
}
2022-03-03 14:37:12.3479348 +0800 CST m=+0.003808501
2009-11-17 20:34:58.651387237 +0800 CST
2009
November
17
20
34
58
651387237
Local
Tuesday
true
false
false
107730h2m13.696547563s
107730.03713792987
6.463802228275793e+06
3.8782813369654757e+08
387828133696547563
2022-03-03 14:37:12.3479348 +0800 CST
1997-08-04 02:32:44.954839674 +0800 CST

时间戳

package main

import (
    "fmt"
    "time"
)

func main() {
    //1970年1月1日0点作为“unix纪元”的原点
	now := time.Now()
	secs := now.Unix()      //秒
	nanos := now.UnixNano() //纳秒
	fmt.Println(now)

	millis := nanos / 1000000 //通多换算纳秒来得到毫秒,因为用秒会不太精确
	fmt.Println("secs  ", secs)
	fmt.Println("millis", millis)
	fmt.Println("nanos ", nanos)

	//将 Unix 纪元起的整数秒或者纳秒转化到相应的时间
	fmt.Println(time.Unix(secs, 0))
	fmt.Println(time.Unix(0, nanos))
}
2022-03-03 14:56:51.977756 +0800 CST m=+0.002675301
secs   1646290611
millis 1646290611977
nanos  1646290611977756000
2022-03-03 14:56:51 +0800 CST
2022-03-03 14:56:51.977756 +0800 CST

时间的格式化和解析

这个用的时候要记得

package main

import (
    "fmt"
    "time"
)

func main() {
    p := fmt.Println

    t := time.Now()
    p(t.Format(time.RFC3339))

    t1, e := time.Parse(
        time.RFC3339,
        "2012-11-01T22:08:41+00:00")
    p(t1)

    p(t.Format("3:04PM"))
    p(t.Format("Mon Jan _2 15:04:05 2006"))
    p(t.Format("2006-01-02T15:04:05.999999-07:00"))
    form := "3 04 PM"
    t2, e := time.Parse(form, "8 41 PM")
    p(t2)

    fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
        t.Year(), t.Month(), t.Day(),
        t.Hour(), t.Minute(), t.Second())

    ansic := "Mon Jan _2 15:04:05 2006"
    _, e = time.Parse(ansic, "8:41PM")
    p(e)
}
2022-03-03T15:02:48+08:00
2012-11-01 22:08:41 +0000 +0000
3:02PM
Thu Mar  3 15:02:48 2022
2022-03-03T15:02:48.660809+08:00
t2 0000-01-01 20:41:00 +0000 UTC
2022-03-03T15:02:48-00:00
parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": cannot parse "8:41PM" as "Mon"

随机数

/**
  @Go version: 1.17.6
  @project: TeachCode
  @ide: GoLand
  @file: rand.go
  @author: Lido
  @time: 2022-03-03 15:10
  @description: Go随机数
*/
package main

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

func main() {

	fmt.Print(rand.Intn(100), ",")
	fmt.Print(rand.Intn(100))
	fmt.Println()

	// 0.0 <= f < 1.0
	fmt.Println(rand.Float64())

	//范围随机数
	fmt.Print((rand.Float64()*5)+5, ",")
	fmt.Print((rand.Float64() * 5) + 5)
	fmt.Println()

	s1 := rand.NewSource(time.Now().UnixNano())
	r1 := rand.New(s1)

	fmt.Print(r1.Intn(100), ",")
	fmt.Print(r1.Intn(100))
	fmt.Println()

	/**
	使用自定义的种子,如果相同则随机数会相同
	*/
	s2 := rand.NewSource(42) //自定义种子
	r2 := rand.New(s2)       //使用自定义的种子
	fmt.Print(r2.Intn(100), ",")
	fmt.Print(r2.Intn(100))
	fmt.Println()
	s3 := rand.NewSource(42)
	r3 := rand.New(s3)
	fmt.Print(r3.Intn(100), ",")
	fmt.Print(r3.Intn(100))
}

数值解析

package main

import (
	"fmt"
	"strconv"
)

func main() {

	//int64 也就是我电脑64位的默认值
	f, _ := strconv.ParseFloat("1.234", 64)
	fmt.Println(f)

	i, _ := strconv.ParseInt("123", 0, 64)
	fmt.Println(i)

	//ParseInt 会自动识别出字符串是十六进制数。
	d, _ := strconv.ParseInt("0x1c8", 0, 64)
	fmt.Println(d)

	u, _ := strconv.ParseUint("789", 0, 64)
	fmt.Println(u)

	//Atoi 是一个基础的 10 进制整型数转换函数
	k, _ := strconv.Atoi("135")
	fmt.Println(k)

	_, e := strconv.Atoi("wat")
	//strconv.Atoi: parsing "wat": invalid syntax
	fmt.Println(e)
}
1.234
123
456
789
135
strconv.Atoi: parsing "wat": invalid syntax

URL 解析

这里好像常用的是协议名称和主机的分割

package main

import (
	"fmt"
	"net"
	"net/url"
)

func main() {

	s := "postgres://user1:pass@host.com:5432/path?k=v#f"
	//s := "https://gobyexample-cn.github.io/url-parsing"

	u, err := url.Parse(s)
	if err != nil {
		panic(err)
	}

	fmt.Println(u.Scheme)//协议名称 postgres

	fmt.Println(u.User)
	fmt.Println(u.User.Username())
	p, _ := u.User.Password()
	fmt.Println(p)

	fmt.Println(u.Host)//host.com:5432
	host, port, _ := net.SplitHostPort(u.Host)
	fmt.Println(host)
	fmt.Println(port)

	//提取路径和 # 后面的查询片段信息
	fmt.Println(u.Path)
	fmt.Println(u.Fragment)

	fmt.Println(u.RawQuery)
	m, _ := url.ParseQuery(u.RawQuery)
	fmt.Println(m)
	fmt.Println(m["k"][0])
}
postgres
user1:pass
user1
pass
host.com:5432
host.com
5432
/path
f
k=v
map[k:[v]]
v

SHA1 哈希

package main

import (
	"crypto/md5"
	"crypto/sha1"
	"fmt"
)

func main() {
	s := "sha1 this string"

	//sha1
	h := sha1.New()
	//md5
	m := md5.New()

	h.Write([]byte(s))
	m.Write([]byte(s))

	bs := h.Sum(nil)
	md := m.Sum(nil)

	fmt.Println(s)
	//SHA1 值经常以 16 进制输出
	fmt.Printf("%x\n", bs)
	//MD5 值经常以 16 进制输出
	fmt.Printf("%x\n", md)
}
cf23df2207d99a74fbe169e3eba035e633b65d94
c0b06bcc2087b93b3be71e1c1a524ce2

base64

package main

import (
	b64 "encoding/base64"
	"fmt"
)

func main() {

	data := "abc123!?$*&()'-=@~"

	//标准base64转码
	sEnc := b64.StdEncoding.EncodeToString([]byte(data))
	fmt.Println(sEnc)

	//标准base64解码
	sDec, _ := b64.StdEncoding.DecodeString(sEnc)
	fmt.Println(string(sDec))
	
	fmt.Println("-----------------------------")

	//URL base64转码
	uEnc := b64.URLEncoding.EncodeToString([]byte(data))
	fmt.Println(uEnc)
	//URL base64解码
	uDec, _ := b64.URLEncoding.DecodeString(uEnc)
	fmt.Println(string(uDec))
}
YWJjMTIzIT8kKiYoKSctPUB+
abc123!?$*&()'-=@~
-----------------------------
YWJjMTIzIT8kKiYoKSctPUB-
abc123!?$*&()'-=@~

参考资料

Go by Example 中文版

秒懂Linux文件权限及chmod命令

Go语言文件的写入、追加、读取、复制操作