go语言学习【十二】


一、命令行参数使用

需求:希望能够获取到命令行输入的各种参数,该如何处理?
答:使用命令行参数

os.Args是一个切片,用来存储所有的命令行参数。

应用案例:编写一段代码,可以获取命令行各个参数。

package main
import(
	"fmt"
	"os"
)
func main(){
	fmt.Println("命令行的参数有:",len(os.Args))
	//遍历os.Args切片,就可以得到所有的命令行输入参数值
	for i,v := range os.Args {
		fmt.Printf("args[%v]=%v\n",i,v)
	}
}

输出结果:

E:\goproject\src\go_code\chapter04\argsdemo02>test.exe tom d:aaa/bbb/init.log 99
命令行的参数有: 4
args[0]=test.exe
args[1]=tom
args[2]=d:aaa/bbb/init.log
args[3]=99

上述使用 os.Args 对参数输入顺序有指定要求,容易出错。方式是比较原生的方式,对解析参数不方便,特别是带有指定参数形式的命令行。
因此,在go语言中可以使用flag包,方便解析命令行参数,而且参数顺序可以随意。

package main

import (
	"fmt"
	"flag"
)
func main() {

	//定义几个变量,用于接收命令行的参数值
	var user string
	var pwd string
	var host string
	var port int

	//&user 就是接收用户命令行中输入的 -u 后面的参数值
	//"u",就是 -u 指定参数
	//"",默认值
	//"用户名,默认为空"对参数的说明
	flag.StringVar(&user,"u","","用户名,默认为空")
	flag.StringVar(&pwd,"pwd","","密码,默认为空")
	flag.StringVar(&host,"h","localhost","主机名,默认为localhost")
	flag.IntVar(&port,"port",3306,"端口号,默认为3306")

	//转换,必须调用该方法
	flag.Parse()
	//从os.Args中解析注册的flag。必须在所有flag都注册好而未访问其值时执行。
	//未注册却使用flag-help时,会返回ErrHelp。

	//输出结果
	fmt.Printf("user=%v pwd=%v host=%v port=%v",
	user,pwd,host,port)
}

输出结果:

E:\goproject\src\go_code\chapter04\flagdemo>test.exe -u root -pwd 123456 -h 127.0.0.5 -port 8080
user=root pwd=123456 host=127.0.0.5 port=8080
//这里可以使用-u/-pwd/-h/-port来任意输入参数,顺序不会影响结果输出。

二、json

(1) json数据格式说明

在JS 语言中,一切都是对象。因此,任何的数据类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组, map,结构体等.
JSON 键值对是用来保存数据一种方式,键/值对组合中的键名写在前面并用双引号""包裹使用冒号:分隔然后紧接着值
{“key1”:val1,“key2”:val2,“key3”:[val3,val4]}
键:属性 ,值:对应的具体的数据

比如:
{“firstName”:“Json”}
{“name”:“tom”,“age”: 18,“address”:[“北京”,“上海”]}
比如:
[{“name”:“tom”,“age”: 18,“address”:[“北京”,“上海”]},
{“name”:“mary”,“age”: 28,“address”:[“广州”,“深圳"]}]

在这个网站上面可以进行JSON在线解析
https://www.json.cn/

在这里插入图片描述
在这里插入图片描述

(2) json的序列化

介绍:json序列化是指,将有key-value结构的数据类型(比如结构体、map、切片)序列化成json字符串的操作。

应用案例:对结构体、map、切片进行序列化,其它数据类型的序列化类似。

package main

import(
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string //`json:"name"` 指定序列化后的名字key是name而不是Name,通过此方式序列化后可以返回想要的值
	Age int //`json:"age"` 解释同上
	Birthday string
	Sal float64
	skill string
}

func testStruct() {
	//对结构体的演示
	var monster = Monster {
		Name:"牛魔王",
		Age:500,
		Birthday:"1600-06-06",
		Sal:10000,
		skill:"牛魔拳",
	}

	//将 monster 序列化 func Marshal(v interface{})([]byte,error)
	data,err := json.Marshal(&monster)
	if err!=nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	fmt.Printf("monster序列化后=%v\n",string(data))
}

//将map进行序列化
func testMap() {
	//定义一个map
	var a map[string]interface{}
	//使用map,需要make
	a = make(map[string]interface{})
	a["name"]="红孩儿"
	a["age"]=8
	a["address"]="洪崖洞"

	data,err := json.Marshal(a)
	if err!=nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	fmt.Printf("a map 序列化后=%v\n",string(data))
}

//对切片进行序列化,这个切片有很多[]map[string]interface{}
func testSlice() {
	var slice []map[string]interface{}
	var m1 map[string]interface{}
	m1 = make(map[string]interface{})
	m1["name"] = "Jack"
	m1["age"] = 18
	m1["address"] = "英国"
	slice = append(slice,m1)

	var m2 map[string]interface{}
	m2 = make(map[string]interface{})
	m2["name"] = "Tom"
	m2["age"] = 22
	m2["address"] = "澳大利亚"
	slice = append(slice,m2)

	data,err := json.Marshal(slice)
	if err!=nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	fmt.Printf("slice 序列化后=%v\n",string(data))
}

//对基本数据类型序列化(这种操作意义不大)
func testFloat64() {
	var num1 float64 = 1234.56

	data,err := json.Marshal(num1)
	if err!=nil {
		fmt.Printf("序列化错误 err=%v\n",err)
	}
	fmt.Printf("num1 序列化后=%v\n",string(data))
}
func main() {
	//演示将结构体,map,切片进行序列化
	testStruct()
	testMap()
	testSlice()
	testFloat64()
}

输出结果:

monster序列化后={"Name":"牛魔王","Age":500,"Birthday":"1600-06-06","Sal":10000}
a map 序列化后={"address":"洪崖洞","age":8,"name":"红孩儿"}
slice 序列化后=[{"address":"英国","age":18,"name":"Jack"},{"address":"澳大利亚","age":22,"name":"Tom"}]
num1 序列化后=1234.56

【注:对于结构体的序列化,如果我们希望序列化后的key的名字由我们自己重新制定,那么可以给struct指定一个tag标签。】

type Monster struct {
	Name string //`json:"name"` 指定序列化后的名字key是name而不是Name,通过此方式序列化后可以返回想要的值
	Age int //`json:"age"` 解释同上
}

(3) json的反序列化

json反序列化是指,将json字符串反序列化成对应的数据类型(比如结构体,map,切片)的操作。

应用案例:将json字符串反序列化成结构体,map和切片。

package main

import(
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string
	Age int
	Birthday string
	Sal float64
	skill string
}

//演示将json字符串,反序列化成struct
func unmarshalStruct() {
	str := "{\"Name\":\"牛魔王\",\"Age\":500,\"Birthday\":\"1600-06-06\",\"Sal\":10000}"

	//定义一个结构体实例
	var monster Monster
	err := json.Unmarshal([]byte(str),&monster)
	if err!=nil {
		fmt.Printf("反序列化错误 err=%v\n",err)
	}
	fmt.Printf("反序列化后 monster=%v\n monster.Name=%v\n",monster,monster.Name)
}

// 反序列化成map
func unmarshalMap() {
	str := "{\"address\":\"洪崖洞\",\"age\":8,\"name\":\"红孩儿\"}"

	//定义一个map
	var a map[string]interface{}

	//反序列化
	//注意:反序列化map时,不需要make,因为make操作被封装到 Unmarshal函数
	err := json.Unmarshal([]byte(str),&a)
	if err!=nil {
		fmt.Printf("反序列化错误 err=%v\n",err)
	}
	fmt.Printf("反序列化后 a=%v\n",a)
}

//反序列化成切片
func unmarshalSlice() {
	str := "[{\"address\":\"英国\",\"age\":18,\"name\":\"Jack\"},"+
	"{\"address\":\"澳大利亚\",\"age\":22,\"name\":\"Tom\"}]"
	
	//定义一个切片
	var slice []map[string]interface{}
	err := json.Unmarshal([]byte(str),&slice)
	if err!=nil {
		fmt.Printf("反序列化错误 err=%v\n",err)
	}
	fmt.Printf("反序列化后 slice=%v\n",slice)
}

func main() {
	unmarshalStruct()
	unmarshalMap()
	unmarshalSlice()
}

输出结果:

反序列化后 monster={牛魔王 500 1600-06-06 10000 }
 monster.Name=牛魔王
反序列化后 a=map[address:洪崖洞 age:8 name:红孩儿]
反序列化后 slice=[map[address:英国 age:18 name:Jack] map[address:澳大利亚 age:22 name:Tom]]

对上面代码的小结:

  • 在反序列化一个json字符串时,要确保反序列化后的数据类型和原来序列化前的数据类型一致。
  • 如果json字符串是通过程序获取到的,或者是从文件中读取到的,则不需要对字符串里的双引号进行转义处理。(因为字符串内部已经转义)

三、单元测试

Go语言中自带有一个轻量级的测试框架testing自带的go test命令来实现单元测试
和性能测试, testing框架和其他语言中的测试框架类似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。
通过单元测试;可以解决如下问题:
1)确保每个函数是可运行,并且运行结果是正确的。
2)确保写出来的代码性能是好的。
3)单元测试能及时地发现程序设计或实现的逻辑错误,使问题及早暴露,便于问题的定位解决。而性能测试的重点在于发现程序设计上的一些问题,让程序能够在高并发的情况下还能保持稳定。

被测试函数

package main
//被测试函数 addUpper
func addUpper(n int) int {
	res := 0
	for i := 1;i <= n-1;i++{
		res += i
	}
	return res
}

//被测试函数 getSub
func getSub(x int ,y int) int {
	return x-y
}

测试 addUpper

package main
import (
	_"fmt"
	"testing" //引入go的testing框架包
)
//编写测试用例,去测试addUpper是否正确
func TestAddUpper(t *testing.T) {
	//调用
	res := addUpper(10)
	if res != 55 {
		t.Fatalf("AddUpper(10) 执行错误,期望值=%v 实际值=%v",55,res)
	}

	//如果正确,输出日志
	t.Logf("addUpper(10) 执行正确......")
}

测试 getSub

package main
import (
	"fmt"
	"testing" //引入go的testing框架包
)

//编写测试用例,去测试addUpper是否正确
func TestGetsub(t *testing.T) {
	//调用
	res := getSub(20,16)
	if res != 4 {
		t.Fatalf("getSub(20,16) 执行错误,期望值=%v 实际值=%v",55,res)
	}

	//如果正确,输出日志
	t.Logf("getSub(20,16) 执行正确......")
}

func TestHello(t *testing.T) { //test测试框架会执行每个以Test开头的测试用例
	fmt.Println("hello everyone")
}

执行结果:
在这里插入图片描述
总结:
1.测试用例文件名必须以_test.go 结尾。比如 cal_ test.go,cal 不是固定的。
2.测试用例函数必须以Test开头,一般来说就是Test+被测试的函数名,比如TestAddUpper。
3.TestAddUpper(t *tesing.T)的形参类型必须是 “*testing.T
4.一个测试用例文件中,可以有多个测试用例函数,比如 TestAddUpper、TestSub、TestHello等。
5.运行测试用例指令
(1)cmd>go test 如果运行正确,无日志,错误时,会输出日志
(2) cmd>go test -v 运行正确或是错误,都输出日志
6.当出现错误时,可以使用t.Fatalf 来格式化输出错误信息,并退出程序。
7.t.Logf 方法可以输出相应的日志
8.测试用例函数,并没有放在main函数中,也执行了,这就是测试用例的方便之处。
9.PASS表示测试用例运行成功,FAIL 表示测试用例运行失败。
10.测试单个文件,一定要带上被测试的原文件。
go test -v cal_test.go cal.go
11.测试单个方法:
go test -v -test.run TestAddUpper


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值