Go语言学习笔记(四)

Go语言学习散记

1.函数命名返回值

Go语言的返回值可以被命名,并且像变量那样使用

返回值的名称应当具有一定意义,可以作为文档使用

如果指定了一个返回值的名字,则可以视为在该函数的第一行中定义了该名字的变量。

func test(x, y int)(a, b int){
    b = x 
    a = y 
    return  //因返回值已命名,此处可以直接省略,自动返回变量 a,b
}

func main(){
    i,j := test(1,2)
    fmt.Println(i)
    fmt.Println(j)
}
//结果为 2  1

2.匿名函数

func(x int) int{
    fmt.Printf("x")
    return x+1
}(1) //匿名函数直接执行


y := func (x int) int { //匿名函数赋名称
    fmt.Println(x)
    return x+1
}
fmt.Println(y(1)) //函数调用

3.defer 语句是Go中一个非常有用的特性,可以将一个方法延迟到包裹该方法的方法返回时执行。在实际应用中,defer语句可以充当其他语言中try...catch...的角色,也可以用来处理关闭文件句柄等收尾操作。

defer执行时机

  • 包裹defer的函数返回时
  • 包裹defer的函数执行到末尾时
  • 所在的goroutine发生panic时

当一个方法中有多个defer时,defer会将要延迟执行的方法“压榨”,当defer被触发时,将所有“压榨”的方法“出栈”并执行。

func test() {
    defer func(){
        fmt.Println("1")    
    }()
    defer func() {
        fmt.Println("2")    
    }()
}

test()
执行结果不是1 2,而是 2 1

4.包的引用

//在hello.go脚本的同级目录下创建文件夹util 其中创建文件conf.go

//conf.go内容

package util //定义包名称,注意是与目录同一个名称,而不是与文件同名称
import "fmt"
func MyPrint(a interface{}){
    fmt.Println(a)
}


//hello.go中引用
方式一
package main
import (
     "./util" //直接引入包(文件夹) 
)

func main(){
    util.MyPrint(1) //调用方式,前面加包名称
}

方式二
package main
import (
    . "./util" //包前面加 . 则再被引用函数时不用加名称
)
func main(){
    MyPrint(1)  // 直接引用
}
方式三
package main
import (
    ut "./util" //包别名,下面引用时用包别名
)
func main(){
    ut.MyPrint(1)
}

注意,包中go文件不能有重名的函数或者变量,外部引用的必须首字母大写。直接在包的go文件中定义的变量可以和函数一样被引用

5.读取文件内容

package main
import (
    "fmt"
    "io/ioutil"
)


func main(){
    data, err := ioutil.ReadFile("./util/data.txt")
    fmt.Println(string(data)) //读取的是byte类数据,要转化为字符串
}

6.数据格式

package main
import "reflect"
a := 1
fmt.Println(reflect.TypeOf(a))
//结果输出为  int

7.当前时间显示

import "time"

a := time.Now().Format(time.RFC3339)
fmt.Println(a)
fmt.Println(reflect.TypeOf(a))
//输出结果 2020-04-01T15:58:28+08:00  string

8. rand.Reader是一个全局,共享的密码用强随机数生成器。

package main 
import(
    "fmt"
    "encoding/base64"
    "crypto/rand"
    "io"
)

//sessionId函数用来生成一个session ID,即session的唯一标识符
func sessionId() string {
    b := make([]byte, 32)
    //ReadFull从rand.Reader精确地读取len(b)字节数据填充进b
    //rand.Reader是一个全局、共享的密码用强随机数生成器
    if _, err := io.ReadFull(rand.Reader, b); err != nil { 
        return ""
    }
    fmt.Println(b) //[62 186 123 16 209 19 130 218 146 136 171 211 12 233 45 99 80 200 59 20 56 254 170 110 59 147 223 177 48 136 220 142]
    return base64.URLEncoding.EncodeToString(b)//将生成的随机数b编码后返回字符串,该值则作为session ID
}
func main() { 
    fmt.Println(sessionId()) //Prp7ENETgtqSiKvTDOktY1DIOxQ4_qpuO5PfsTCI3I4=
}

9.sha256加密

package main
import (
    "fmt"
    "crypto/sha256"
)
func main(){
    s := "1" 
    h := sha256.New()
    h.Write([]byte(s))
    bs := h.Sum(nil)
    fmt.Printf("%x\n",bs)
}
等同于python的
import hashlib
hashlib.sha256("1").hexdigest()

10. 十六进制实现十六进制编码和解码

import "encoding/hex"

hash := []byte("Hello")

fmt.Printf("%x\n", hash)
h := hex.EncodeToString(hash)
fmt.Println(h) 
以上两行效果相同,将ASCII编码转换为16进制字符串形式展示

将16进制字符串转为ASCII编码
byterun, err := hex.DecodeString(h)

11.将参数转义为url参数

import "net/url"

params1 := make(map[string]string)
params1["a"] = "aa "
params1["b"] = "bb"
parameters := url.Values{}
for key, val := range params1 {
	parameters.Add(key, val)
}
fmt.Println(parameters)
结果是a=aa&b=bb
等同于python的urllib.urlencode({"a":"aa","b":"bb"})

12.浮点类型

1.声明和赋值
var f1 float32
f1 = 12

f2 := 12.0 变量f2会自动推导为float64

f1 = f2 类型不同会编译错误
f1 = float32(f2) 可行

2.浮点数比较,浮点数不是一种精确的表达方式,所以像整形那样直接用==来判断两个浮点数是否相等是不可行的,这可能会导致不稳定的结果。
以下是替代方案

import "math"

func IsEqual(f1, f2, p float64) bool { 参数p为用户定义的比较精度,比如0.00001
    return math.Abs(f1-f2) < p
}

13.http请求

import "net/url"

endpoint := "https://10.154.197.217:32621/v1/chaincode/operation/v1/chaincode/operation?a=aa&b=bb"
URL, err := url.Parse(endpoint)

URL.String() //全路径https://10.154.197.217:32621/v1/chaincode/operation/v1/chaincode/operation?a=aa&b=bb

URL.Path //路径 /v1/chaincode/operation/v1/chaincode/operation
URL.RawQuery //GET参数 a=aa&b=bb

//三个参数 请求方式"POST" string,   请求地址string,  请求的内容io.Reader
req, err := http.NewRequest(method, URL.String(), bytes.NewReader(body)) 
if err != nil {
    return nil, err
}
//设置请求内容为json
req.Header.Set("Content-Type", "application/json") 
//header中添加额外参数
for key, value := range headers{
    req.Header.Set(key, value)
}

req.Close = true
//发起请求
resp, err := client.Do(req)

defer ReleaseBody(resp) 最后关闭请求


func ReleaseBody(resp *http.Response) { //关闭请求
	if resp != nil && resp.Body != nil {
		resp.Body.Close()
	}
}

import "net/http"
func IsResponseStatusOk(resp *http.Response) bool { //判断返回值是否正常200
	return http.StatusOK == resp.StatusCode/100*100
}

body, err := ioutil.ReadAll(resp.Body) //读取返回的内容,[]byte类型

设置返回值
一、json格式
func responseAction(w http.ResponseWriter, errcode, errmsg string){
	result := map[string]string{
		"errcode": errcode,
		"errmsg": errmsg,
	}
	w.Header().Set("Content-Type", "application/json")
  	json.NewEncoder(w).Encode(result)
}
responseAction(w, "4001", "接口验证错误") //在处理请求的函数中,调用此函数,即可设置返回值
二、返回文本内容
io.WriteString(w, "hello")
三、返回请求错误
http.Error(w, "请求错误", 400) //指定错误码400
http.NotFound(w, r) //返回404

14.字符串去掉头尾的某些字符

import "strings"

strings.TrimRight("ababa", "ba") --> "" 
strings.TrimLeft("ababa", "ba") --> ""
strings.TrimSuffix("ababa", "ba") --> "aba"
strings.TrimPrefix("ababa", "aba") --> "ba"
strings.Trim("a12a3ababa", "ba") --> "12a3"
strings.TrimSpace(" a 1 ") --> "a 1" //消除两侧空格
/*
Trim,TrimRight,TrimLeft,均是拿第二个参数排列组合形成字符串子集,然后Trim从左右两侧,TrimRight,TrimLeft分别从右和左侧,开始匹配,直到没有匹配到则删除字符串停止。
TrimSuffix,TrimPrefix是第二个参数直接哪来用,并不匹配子集。因此消除字符串可以常用这两个函数
*/

//将字符串分割成切片
b = "a+b"
strings.Split(b, "+")
/*
结果是 ["a", "b"]
*/

15.big.Int的字符串操作

import "math/big"

a := new(big.Int)


//字符串转化为big.Int类型,函数的第二个参数是进制,2,8,10,16
a, _ = a.SetString("105353974345752633441528052148797415287128357633020936715230179936754399715492", 10)

//转化为字符串
a.String()

16.值语义和引用语义

b = a     b.Modify()

如果b的修改不影响a的值,那么此类型属于值类型。如果会影响a的值,那么此类型就是引用类型。

大多数类型是值语义,可以将Go看做是值语义

  • 基本类型:byte,int,bool,float32, float64, string等
  • 复合类型:数组,结构体,指针

四类特殊,看起来像引用语义。(因为下面四类实现方式本质上是指针)

  • 数组切片
  • map
  • channel
  • 接口interface

17.匿名组合(面向对象的继承)。Go语言也提供了继承,但是采用了组合的文法,所以我们将其称为匿名组合。


type Son1 struct { //子类一
    Name string
}

func (son1 Son1) Do1() {
    fmt.Println("s111d11111")
}

func (son1 Son1) Do2(){
    fmt.Println("s111d22222")
}

func (son1 Son1) Do3(){
    fmt.Println("s111d33333")
}

type Son2 struct { //子类二
    Name string
}

func (son2 Son2) Do1() {
    fmt.Println("s222d1111")
}

type Father struct { //父类
    Name string
    Son1
    Son2
}

func (f1 Father) Do2(){
    fmt.Println("ffffff")
}

func main(){
    f := Father{}
    f.Do3()
    f.Son1.Do3() //与f.Do3()效果完全相同
//    fmt.Println(foo.Name)

}
/*
1.注意组合的语法是直接写结构体名称,而不是写    son Son1 这样的成员属性
type Father struct { 
    Name string
    son Son1  //此种写法只是成员属性,而不是组合(继承)
}
2.使用特点,直接使用子类的成员属性和方法。如父类Father没有方法Do3,但是可以直接写f.Do3(),效果与f.Son1.Do3()完全一样
3.如果访问属性或方法时,父类有对应的属性和方法,则直接调用父类的属性和方法,如果子类有相同的属性或方法,可以理解为父类的重写,如Do2()
4.如果有两个以上的子类组合在一起,且两个子类有相同的方法或属性(如Do1()),且父类没有对应的方法或属性,在调用时会发生编译错误,不调用不报错。

*/

18.ecdsa加密中获取公钥证书

方式一,从私钥对象中获取

keyPEMBlock, _ := ioutil.ReadFile(privateKeyPath)
keyDERBlock, _ := pem.Decode(keyPEMBlock) //解码出对应的block值
privateKey, _ := x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes) //pkcs8格式的私钥解析

publicKey := &privateKey.(*ecdsa.PrivateKey).PublicKey //断言后转换类型,获取属性,从私钥中获取

方式二,从证书文件中获取,crtPath证书路径
certPEMBlock, err := ioutil.ReadFile(crtPath)
certDERBlock, _ := pem.Decode(certPEMBlock)
x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes) 
publicKey = x509Cert.PublicKey.(*ecdsa.PublicKey) //断言后转换类型为*ecdsa.PublicKey得到

19断言,用于接口转换。如接口A和接口B,如果A中所有方法时B中方法的子集,那么接口B可以赋值给接口A。(一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口。)

情况一,直接使用断言(在一定为真的情况下,如果不是则会报错)
c := a.(string) //a是string类型的情况下,直接使用返回的值时a本身。

方式二,判断是否为真
value, ok := a.(string) //两个变量接收时,第一个变量是a本身,第二个变量ok是bool值,为string类型为真
if ok{
    fmt.Println("类型是string")
}else{
    fmt.Println("类型不是string")
}

不用断言会抛出异常的例子
func test(a interface{}) string {
    return string(a)
}
抛出异常:cannot convert a (type interface{}) to type string: need type assertion
应改为
func test(a interface{}) string {
    value, ok := a.(string) //断言,1.在调用时test("abc") 此时空接口可以被赋值为字符串。2.执行到函数内此行时,a已经是string类型,可以通过断言,实际流程是,string类型转换为string类型。
    if !ok {
        fmt.Println("It's not ok for type string")
        return
    }
    return value
}
接口相当于一个主体套了一层衣服,有选择的释放某些能力,如果接口包含的功能多于主体,那主体就不能套上这个衣服。
断言的接口转换相当于为已经套上衣服的主体换衣服。能不能换仅取决于主体是否具有要换的新接口的能力,与原接口无关。
type B struct { //主体
}
主体具有的两个能力
func (b B)F1(){
    fmt.Println("BBBFF11")
}
func (b B)F2(){
    fmt.Println("BBBFF22")
}
主体的两套衣服
type Inf1 interface {
    F1()
}

type Inf2 interface {
    F1()
    F2()
}

a := new(B) //主体
var b Inf1 = a //套上衣服Inf1
此时套上衣服的主体不再有能力F2(), 即b.F2()会报错。

c := b.(Inf2) //用断言为主体换衣服为Inf2。因Inf2中功能在主体B中都有,所以可以换衣服成功。
c.F2() //此此时新衣服Inf2中有F2(),主体可以有功能F2()

20 gzip压缩

package main
import (
    "fmt"
    "bytes"
    "compress/gzip"
    "encoding/hex"
)

func gzipAction(data string) string { //压缩
    var b bytes.Buffer
    w := gzip.NewWriter(&b)
    defer w.Close()
    _, err := w.Write([]byte(data))
    if err != nil {
        return ""
    }
    w.Flush()
    out := hex.EncodeToString(b.Bytes())
    return out
}

func freeGzip(signature string) (data string, err error) { //解压
    byterun, err := hex.DecodeString(signature)
    if err != nil {
        return
    }
    r, err := gzip.NewReader(bytes.NewBuffer(byterun))
    if err != nil {
        fmt.Println("this is error")
        return
    }
    defer r.Close()

    buf := make([]byte, 1024)
    count, err := r.Read(buf)
    if err != nil {
        fmt.Println("decode = ", err)
        return
    }
    return string(buf[:count]), nil
}

func main(){
    data := "h"
    gz := gzipAction(data)
    fmt.Println(gz)
    
    fg,_ := freeGzip(gz)
    fmt.Println(fg)
}

python2.7压缩

import gzip
import io

def gzip_str(string_): //压缩
    out = io.BytesIO()

    with gzip.GzipFile(fileobj=out, mode='w') as fo:
        fo.write(string_.encode())

    bytes_obj = out.getvalue()
    return bytes_obj

def gunzip_bytes_obj(bytes_obj): //解压
    in_ = io.BytesIO()
    in_.write(bytes_obj)
    in_.seek(0)
    with gzip.GzipFile(fileobj=in_, mode='rb') as fo:
        gunzipped_bytes_obj = fo.read()

    return gunzipped_bytes_obj.decode()
    
    
string_ = 'h'


gzipped_bytes = gzip_str(string_)
print gzipped_bytes.encode('hex')

original_string = gunzip_bytes_obj(gzipped_bytes)
print original_string

python和go的gzip压缩结果不同

21. 读取http中数据

r.Form.Get("lang") //注意要返回字符串需要用Get方法
r.PostForm["lang"]
r.FormValue("lang")
r.PostFormValue("lang")

r.Form和r.PostForm必须在调用r.ParseForm()之后,才会有数据,否则则是空数组。
而r.FormValue和r.PostFormValue("lang")无需ParseForm的调用就能读取数据。

ParseForm会将body当作表单解析,并将结果既更新到r.PostForm也更新到r.Form,而r.Body是io.ReadCloser类型,只能读取一次。
一旦调用r.ParseForm(),body, err := ioutil.ReadAll(r.Body)的结果就是空
r.FormValue和r.PostFormValue源代码中是调用了r.ParseForm的

所以,如果要body是json数据,且url中有参数可以采取以下写法

queryValues := r.URL.Query()
lang :=  queryValues.Get("lang")
body, err := ioutil.ReadAll(r.Body)

这样GET参数和body中json均可以读取出来

22.bytes操作

bytes.IndexAny /*把b解析为UTF-8编码的字节序列,返回chars中任何一个字符在b中第一次
出现的索引位置;如果s中不包含chars中任何一个字符,则返回-1.*/
import "bytes"
a := "hello"
b := []byte(a)
c := bytes.IndexAny(b, "h") //返回0

23.字符串比较

fmt.Println("go"=="go")
fmt.Println("GO"=="go")

fmt.Println(strings.Compare("GO","go")) //不相同返回-1,区分大小写,速度比==快
fmt.Println(strings.Compare("go","go"))

fmt.Println(strings.EqualFold("GO","go")) //不区分大小写

输出
true
false
-1
0
true
1,自建方法“==”,区分大小写,最简单的方法
2,Compare函数,区分大小写,比自建方法“==”的速度要快
3,EqualFold比较UTF-8编码在小写的条件下是否相等,不区分大小写

24. panic和recover的使用原理

func main() {
    GO()
    PHP()
    PYTHON()
}

func GO() {
    fmt.Println("I am Go, nothing happen")
}

func PHP(){
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("get the panic==", err)
            fmt.Println("sys going to recover...")
        }   
    }() 
    panic("now panic out")
    fmt.Println("back off the panic, never show")
}

func PYTHON(){
    fmt.Println("if recover, you will see me")
}

25,自定义error

err := fmt.Errorf("这是错误信息") //error类型,定义error的文本提示

panic(fmt.Errorf("这里是错误信息")) //抛出异常,终止程序

26.数组中放入多种类型

j := make([]interface{}, 0)
j = append(j, "a")
j = append(j, 1)
注意此时切片的元素是接口,如果要给其它声明的变量赋值需要断言

猜你喜欢

转载自blog.csdn.net/u010145988/article/details/105202249
今日推荐