小工具      在线工具  汉语词典  dos游戏  css  js  c++  java

Go语言流处理,工厂模式,命令参数,序列化,单元测试

Go,golang 额外说明

收录于:23天前

IO流

流是数据在数据源和程序之间所采用的路径。数据源可以是文件数据库或者键盘输入等,程序是运行在内存中的应用程序。

数据从数据源输入到程序的路径是输入流,数据从内存输出到数据源的路径是输出流。

在这里插入图片描述

流式传输基于内存。内存的输入是输入流,数据在内存中的持久化是输出流。

计算机中的流实际上是信息的转换。它是一个有序的流,所以相对于某个对象,我们通常称该对象从外界输入信息(Input),相应地,该对象向外界输出的信息(Output)称为输出流,统称为输出流。称为输入/输出流(I/O Streams)。对象之间交换信息或数据时,总是将对象或数据转换为某种形式的流,然后通过流的传输,流到达目的对象后转换为对象数据。因此,流可以看作是一种数据载体,通过流可以实现数据的交换和传输。

对于计算机来说,流的基本单位是字节流,但是对于程序来说,程序也可以识别字符流。字节流是面向计算机的,字符流是面向应用程序的。

该程序还通过流的方式实现与计算机的数据交换。通常,编程语言提供内置的流(I/O)接口。

Java I/O

alt

Go I/O
在这里插入图片描述
在这里插入图片描述

在计算机软件的工作过程中,需要大量的I/O。例如,软件的本地数据库存储用户的缓存信息等。流是数据持久化的桥梁。将程序中的数据,即内存中的数据写入到显示器或文件系统中。

键盘输入输出

//输出
fmt.Println()   //打印换行
fmt.Print()     //打印
fmt.Printf()    //格式化打印


//输入
fmt.Scanf()
fmt.Scanln()
fmt.Scan()

格式化输入和输出(通常用f)

%v 值的默认格式表示
%T 值的类型的Go语法表示
%t 单词true或false
%d 表示为十进制
%f 有小数部分但无指数部分,如123.456
%s 直接输出字符串或者[]byte

fmt.Printf(format string, a ...interface{}) (n int, err error)

var a = "test"
var b = Student{
    } 
var c = "10"
fmt.Printf("默认输出%v,类型输出%T,十进制输出%d,字符输出%s", a, b, c, a)

在这里插入图片描述

fmt.Scanf(format string, a ...any) (n int, err error)

在格式化输入时,需要注意以下几点:

  1. 输入的格式必须与书面格式一致,与格式化程序相对应即可。
  2. 输入时必须使用接收地址以节省内存。
  3. 书写时,格式化字符不使用标点符号分隔,输入时默认使用空格分隔。
//3特性
var a1 int
var b1 string
fmt.Scanf("%d,%s\n", &a1, &b1)
fmt.Scanf("%d%s\n", &a1, &b1)

输入时,第一个用“,”分隔,第二个用空格分隔。

//特性1
fmt.Scanf("a=%d,b=%s\n", &a1, &b1)
fmt.Scanf("输入一个十进制数%d,输入一个字符串%s\n", &a1, &b1)

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

标准输入和输出

func Println(a ...interface{}) (n int, err error)

a1 := 10
b1 := "qwe"
fmt.Println(a1, b1)

在这里插入图片描述
Println方法标准化输出时相邻参数的输出之间添加空格并在输出结束后添加换行符,Print不会自动换行。

func Scan(a ...interface{}) (n int, err error)

Scan 扫描标准输入中的文本,并将成功读入的以空格分隔的值保存到成功传递给该函数的参数中。 (注意只能用空格分隔输入,并且书写时只能用“,”分隔变量地址接收)

var a1 int
var b1 string
fmt.Scan(&a1,&b1)
fmt.Print(a1, b1)

在这里插入图片描述
Scanln方法能自动换行。

fmt包其他格式化I/O,详见中文开发手册

文件操作

file, err := os.Open("D:\\Go\\Go Files\\unit5\\src\\test\\hello.txt")
//file, err := os.Open("D:/Go/Go Files/unit5/src/test/hello.txt")
//file, err := os.Open("../test/hello.txt")
if err != nil {
    
	fmt.Println("file nnot found")
	return
}
defer file.Close()

reader := bufio.NewReader(file)

for {
    
	str, err := reader.ReadString('\n')
	if err == io.EOF {
    
		break
	}

	fmt.Print(str)
}

对于Go来说没有严格的/\\区分,均可以读取。

filebyte, err := ioutil.ReadFile("D:\\Go\\Go Files\\unit5\\src\\test\\hello.txt")
if err != nil {
    
	fmt.Println("read file err=", err)
}
fmt.Printf("%v", string(filebyte))
  • 创建文件并写入文件
filepath := "./abc.txt"
file, err := os.OpenFile(filepath, os.O_RDONLY|os.O_CREATE, 0666)
if err != nil {
    
	fmt.Printf("open file err=%v\n", err)
	return
}

defer file.Close()

str := "hello,gardon\n"

writer := bufio.NewWriter(file)
//循环写入几行
for j := 0; j < len(str); j++ {
    
	writer.WriteString(str)
}

writer.Flush()

工厂模式

在Go语言中,一条件下某些结构体并不需要多次创建,只需要单一的实例即可即工厂模式或者单例模式。例如在数据的datasource创建时的工厂模式单例即可。

Go语言中没有像Java那样的static关键字。如果需要单例模式,只需要通过指针进行操作,即返回变量的地址。

package main

import "fmt"

func main() {
    
	//
	var student = StudentFactory(1, "xuwenhui")
	fmt.Println((*student).id, (*student).name)
}

type student struct {
    
	id   int
	name string
}

func StudentFactory(id int, name string) *student {
    
	return &student{
    
		id:   id,
		name: name,
	}
}

在上面代码中student结构体要设为单例模式,需要设置访问权限,通过公开访问方法StudentFactory返回结构体的地址,并用指针操作结构体,实现工厂模式。

类型断言

在Go的多态特性中,不同的特性需要使用断言来调用不同的方法。

type Person struct {
    
	name string
	sex  string
}

type Student struct {
    
	person Person
	sid    string
}

// 需要一个父类是两个结构体的父接口
// 空接口是任何类型的父类
type Per interface {
    
}

在上述代码中需要一个参数能同时接收Person和Student类,同时调用其方法,Go语言中,通过断言来决策。

断言能够将子类用父类接收实现多态性,具体用法是变量.(类型)

func main() {
    
	var a Per
	var b = a.(Person)
	fmt.Println(b.name, b.sex)

	var c = a.(Student)
	fmt.Println(c.person.name, c.person.sex, c.sid)
}

type Person struct {
    
	name string
	sex  string
}

type Student struct {
    
	person Person
	sid    string
}

// 需要一个父类是两个结构体的父接口
// 空接口是任何类型的父类
type Per interface {
    
}

上面的代码中,a接口通过断言转换为任意类型(Person和Student)。使用断言转换任何已知类型并调用转换后的成员和方法。

package main

import "fmt"

func main() {
    
	// var a Per
	// var b = a.(Person)
	// fmt.Println(b.name, b.sex)
	// b.showPn()

	// var c = a.(Student)
	// fmt.Println(c.person.name, c.person.sex, c.sid)
	// c.showStu()

	stu1 := Student{
    
		person: Person{
    
			"xu",
			"nan",
		},
		sid: "s001",
	}

	identufy(stu1)

}

type Person struct {
    
	name string
	sex  string
}

func (pn Person) showPn() {
    
	fmt.Println(pn.name, pn.sex)
}

type Student struct {
    
	person Person
	sid    string
}

func (stu Student) showStu() {
    
	fmt.Println(stu.person.name, stu.person.sex, stu.sid)
}

// 需要一个父类是两个结构体的父接口
// 空接口是任何类型的父类
type Per interface {
    
}

func identufy(per Per) {
    
	//通过断言做类的变换
	var x = per.(Student)
	fmt.Println(x.person.name, x.person.sex, x.sid)
	x.person.showPn()
	x.showStu()

}

在上面代码中identufy方法接收Per的参数,使用断言将参数转化为已指对象Student,就可以使用其成员与方法。同时也可以接收Person参数,或者接收任意已知类型。

命令参数

在多环境开发的情况下,需要更改环境以方便测试,因为不同的环境配置是不同的。这可以直接通过读取代码内部的不同配置文件来实现,但是代码打包后,就无法通过更改代码来实现了。 ,需要传递外部参数。

Go的os命令提供了os.Args切片来存储所有命令行参数。

import (
	"fmt"
	"os"
)

func main() {
    

	//输出命令行参数长度
	fmt.Println("命令行参数长度", len(os.Args))
	//循环输出参数
	for index, value := range os.Args {
    
		fmt.Printf("args[%v]=%v\n", index, value)
	}

}

在这里插入图片描述

请注意,参数之间用空格分隔。

命令参数解析

flag包实现了命令行参数的解析。

os.Args可以满足一些基本的参数要求,但是复杂的在使用改参数就不太方便了,Go也提供了flag包实现了命令行参数的解析。

var usr, pwd, host string
var port int

flag.StringVar(&usr, "user", "", "用户名")
flag.StringVar(&pwd, "password", "", "密码")
flag.StringVar(&host, "h", "localhost", "主机名")
flag.IntVar(&port, "port", 3306, "端口")

flag.Parse()

fmt.Printf("usr=%s,pwd=%s,host=%s,port=%d", usr, pwd, host, port)

flag的var系列的函数将参数绑定到指定的变量上,通过变量输入没有顺序要求。var系列函数有一般有四个参数第一参数为程序的变量,第二个参数为绑定输入变量参数,第三个为默认值,第四个参数为绑定参数描述。

命令行输入时通过-绑定参数 数值输入。

在这里插入图片描述

序列化与反序列化

序列化

Go语言对JSON操作的包均在encoding/json包下,包下提供了Marshal方法来序列化Go内置对象。Marshal函数返回v的json编码。

func Marshal(v interface{}) ([]byte, error)

//创建结构体
type Person struct {
    
	name    string
	address string
}


//序列化
per := Person{
    
	name:    "钢铁侠",
	address: "漫威",
}
perjson, err := json.Marshal(&per)
if err != nil {
    
	panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))

在这里插入图片描述
上面代码中序列化了一个Person对象,但打印时却打印失败,这是由于结构体成员都是小写的,只能在本包内访问,序列化没有意义,也不支持私有类的序列化。

type Person struct {
    
	Name    string
	Address string
}

将结构体改为公共类时就可以序列化了。
在这里插入图片描述

与 Java 万物皆对象的本质不同,Go 除了结构体之外,还有映射、数组、切片等。

//定义一个map类型
func initMap() map[string]string {
    
	var tmp map[string]string = map[string]string{
    }
	tmp["1"] = "aaa"
	tmp["2"] = "bbb"
	tmp["3"] = "ccc"
	return tmp
}

//对map序列化
a := initMap()

ajson, err := json.Marshal(&a)
if err != nil {
    
	panic("ajson序列化失败")
}
fmt.Printf("%v\n", ajson)
fmt.Println(string(ajson))


在这里插入图片描述

//输出序列化时的数据类型
fmt.Printf("%T\n", ajson)
fmt.Printf("%T\n", string(ajson))

在这里插入图片描述
可以看出序列化后时一个字节数组,通过string()函数将其转化为字符串,即为json字符串。

Go序列化时tag标签的使用

序列化时,成员变量的名称全部大写。但在实际使用中,需要统一的书写标准和具体的名称,这就需要在序列化时使用标签。

{
    "Name":"钢铁侠","Address":"漫威"}

Go语言结构体序列化tag使用规则是在结构体字段后添加json:"name"并用反引号包裹。

type Person struct {
    
	Name    string `json:"person_name"`
	Address string `json:"person_address"`
	Age     int
}

如果在“名称”和“地址”中使用标签,它们的序列化名称将成为自定义的 person_names。 Age成员不使用标签,因此不会改变。

per := Person{
    
	Name:    "钢铁侠",
	Address: "漫威",
	Age:     18,
}
perjson, err := json.Marshal(&per)
if err != nil {
    
	panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))

在这里插入图片描述
struct的tag只会在序列化时起作用不会影响结构体的使用。

反序列化

反序列化就是将序列化后的json字符串或者字节数组转换成编程语言内置的数据结构。这种反序列化可以在相同语言或不同语言中发生。例如,Java类被序列化,然后通过网络传输到前端进行json反序列化,或者任何其他后端语言序列化内置数据,然后传输。到前端。

反序列化序列化数据的语言相同,Java实现和Go实现。

/** go语言实现 */

//定义结构体
type Person struct {
    
	Name    string `json:"person_name"`
	Address string `json:"person_address"`
	Age     int    `json:"person_age"`
}

//序列化
per := Person{
    
	Name:    "钢铁侠",
	Address: "漫威",
	Age:     18,
}
perjson, err := json.Marshal(&per)
if err != nil {
    
	panic(err)
}
fmt.Printf("per序列化的json数据为为:%v", perjson)
fmt.Printf("per序列化的json字符串为:%v", string(perjson))

在这里插入图片描述
序列化后为一个字节数组,可以通过string()方法转化为字符串。

func json.Unmarshal(data []byte, v any) error
该方法时反序列化方法,第一个参数为需要反序列化的字节数组,第二个参数为反序列化后数据的赋值变量。

//反序列化赋值变量
var per1 Person
err1 := json.Unmarshal(perjson, &per1)
if err1 != nil {
    
	panic(err1)
}
fmt.Printf("反序列化的数据per1%s,%s,%d", per1.Name, per1.Address, per1.Age)

在这里插入图片描述

str := `{"person_name":"钢铁侠","person_address":"漫威","person_age":18}`

var per2 Person
err2 := json.Unmarshal([]byte(str), &per2)
if err2 != nil {
    
	panic(err2)
}
fmt.Println(per2)

对字符串也可以反序列化,需要将字符串转化为字节数组,通过[]byte()方法。

单元测试

在Go语言包中,需要测试某些方法是否正确,某些业务逻辑是否合理。这些测试需要通过。一般需要在主包中引入模块,然后测试模块的方法或逻辑。这样做的缺点是测试代码需要写在主类中。但在实际项目开发中,主类有自己的逻辑。在主类中编写测试代码非常繁琐。另外,项目运行时,不能写在主类中。修改类中的测试代码。而且,这些测试代码在生产环境中需要删除。

为了解决这些问题,Go语言提供了单元测试,可以对包和模块的某些方法进行编程代码测试。而且这些测试模块都是独立的,不需要写在主类中,不会影响主要业务和代码。部署生产环境时,可以直接删除测试模块的代码,非常方便。

Go语言中提供了testing单元测试框架,能够很好的解决这些问题。testing单元测试框架提供go test命令来实现单元测试和性能测试,基于这个框架针对相应的函数写测试用例。

testing 提供对 Go 包的自动化测试的支持。通过 go test 命令,能够自动执行如下形式的任何函数:

func TestXxx(*testing.T)

其中 Xxx 可以是任何字母数字字符串,用于识别测试用例。要编写一个新的测试套件,需要创建一个名称以 _test.go 结尾的文件,该文件包含 TestXxx 函数,如上所述。 将该文件放在与被测试的包相同的包中。该文件将被排除在正常的程序包之外,但在运行 “go test” 命令时将被包含。

单元测试的机制是编译器创建一个主类,该类将引入xxx_test.go包并调用TestXXX测试方法。这些过程都是由测试框架完成的。

因此,在使用测试框架时,必须按照其规范编写测试用例。

测试用例规范

  1. 测试的包必须使用_test.go后缀结尾;
  2. 测试包需要引入testing
  3. 测试方法必须以Test开头即TestXXX,且参数为测试包的的指针参数,一般为*testing.T

在这里插入图片描述

//mathutil.go

package mathutil

func add(x int, y int) int {
    
	return x + y
}

func sub(x int, y int) int {
    
	return (x - y)
}
//mathutil_test.go

package mathutil

import (
	"fmt"
	"testing"
)

func TestAdd(t *testing.T) {
    
	res := add(10, 13)
	if res != (10 + 13) {
    
		fmt.Println("add Fail,expect 23 but found", res)
	}
}

在这里插入图片描述

总结

  1. 测试用例文件名必须以_test.go结尾;
  2. 测试用例函数必须以Test开头;
  3. 测试函数的参数类型必须是*testing.T

在这里插入图片描述

  1. 运行测试用例的指令为go test无日志信息;go test -v打印详细信息
  2. 使用*testing.T的方法t.Fatalf打印错误退出程序和t.Logf方法可以打印日志。
  3. 测试用例独立于主程序,仅在go test命令中生效。 PASS表示运行成功,FAIL表示运行失败。
. . .

相关推荐

额外说明

1.特定领域知识图谱知识融合方案(实体对齐):商品知识图谱技术实战

知识图谱专栏简介:数据增强,智能标注,文本信息抽取(实体关系事件抽取)、知识融合算法方案、知识推理、模型优化、模型压缩技术等

额外说明

RabbitMq分布式事务解决方案第一篇

前言 微服务架构下,相比单机事务,一个比较复杂的地方在于,在分布式环境下,面对的是分布式事务,分布式事务整体来说无法严格遵循传统的ACID4个特性,而只能根据系统的业务指标,通常满足可用性,和最终一致性,这也是不少互联网产品的实践结果的选择 在分布式事务

额外说明

Qt自定义控件之动画文本

文章目录 前言 一、动画文本的效果 二、具体实现 定义动画对象 设置动画时长的实现 设置text函数实现 绘制代码 设置字体函数 三、高级部分 操作代码 总结 前言 在 Qt 中,自定义控件可以让我们实现丰富的用户界面效果和交互体验。其中,动画文本是一种

额外说明

vulnhub打靶-XXE

vulnhub靶机-XXE 1.下载靶机,启动靶机,扫描地址 下载地址:vulnhub–XXE nmap -sS 192.168.2.0/24 //这里我是虚拟机192.168.2.0网段 扫描出来发现主机IP nmap -sS -sV 192.168

额外说明

mysql:解压版MySQL通过SQLyog可视化密码过期问题(错误号码1862)

一.发现问题 通过之前的配置安装MySQL,cmd显示我已经成功安装MySQL 但是通过show databases; 我这里看不到到数据库的东西 通过SQLyog连接的时候也出现了问题。 通过查询发现,错误号码1862的意思: 二.解决问题 一开始认为

额外说明

Dism命令安装组策略(gpedit.msc)

        运行命令gpedit.msc发现找不到文件,因为自己使用的系统是Windows 10家庭中文版本,没有直接安装,但是组策略的文件还是在系统里面,只是没有安装而已,下面通过自带的Dism来安装组策略。 @echo off pushd "%~

额外说明

Power BI 傻瓜入门 15. DAX功能带来乐趣

本章的内容包括: 了解DAX中的功能 使用DAX函数制作定义明确的公式 发现哪些DAX函数可以帮助解决复杂的数据计算或操作需求 在第14章中,我将讨论函数如何成为计算表达式中命名公式的一部分。作为提出计算表达式的人,您是为函数提供特定参数的人——有些是必

额外说明

Notepad++开源替代NotepadNext

对于Windows用户来说,提到文本编辑软件,我们第一个想到的就是Notepad++。 Notepad++是一款免费的开源文本编辑软件,支持windows系统、支持中文版。作为文本编辑时比windows自带的记事本更强大,除了适合作为一款轻量型的文本编辑

额外说明

emoji语言常用图标收集(有趣的emoji)

绘文字(日语:絵文字/えもじ ,读音即emoji)是日本在无线通信中所使用的视觉情感符号,绘指图画,文字指的则是字符,可用来代表多种表情,如笑脸表示笑、蛋糕表示食物等。在中国大陆,emoji通常叫做“小黄脸”,或者直称emoji,emoji就是表情符号。

额外说明

AI-TestOps —— 软件测试工程师的一把利剑

写在前面 软件测试的前世今生 测试工具开始盛行 AI-TestOps 云平台 ● AI-TestOps 功能模块 ● AI-TestOps 自动化测试流程 写在前面 最近偶然间看到一句话:“软件测试是整个 IT 行业中最差的岗位”。这顿时激起了我对软件测

ads via 小工具