GO 重新进阶学习(二)

传统的数据类型断言

在GO1.8之前并没有泛型,如果用复杂的数据结构,支持多类型的输入输出,只能用类型匹配,进行操作
接下来是泛型的学习
这是官方链接

https://go.dev/doc/tutorial/generics

很多东西都可以直接看官方文档,因为GO语言更新速度挺快的。
然后找到一篇介绍泛型不错的文章

https://zhuanlan.zhihu.com/p/503093691
https://segmentfault.com/a/1190000041634906

實現一個自定義的array,只能存int flout
然後實現一個數組中返回最大最小或求和

package main

import (
	"fmt"
)

//这样就实现了自定义的array 可以储存不同值

type MyArrayr interface {
	Set(value interface{})
	Get(position int) (value interface{})
}

type myArray struct {
	ArrayCustomize []interface{}
}

//这个空接口必要的类型判断真的巨麻烦

func (m *myArray) Set(value interface{}, position int) {
	switch t := value.(type) {
	case int:
		m.ArrayCustomize[position] = value
	case float64:
		m.ArrayCustomize[position] = value
	case nil:
		fmt.Printf("%T", t)
	default:
		fmt.Println("unknown")
	}
}

func (m *myArray) Get(position int) (value interface{}) {
	return m.ArrayCustomize[position]
}

func main() {
	var myA = new(myArray)
	myA.ArrayCustomize = make([]interface{}, 10)
	var emptyInterface interface{} = 9.0
	var emptyInterfaceTwo interface{} = 1

	myA.Set(emptyInterface, 0)
	myA.Set(emptyInterfaceTwo, 1)

	var value = myA.Get(0)
	var valueTwo = myA.Get(1)

	fmt.Printf("%T %v", value, value)
	fmt.Printf("%T %v", valueTwo, valueTwo)
}

泛型

只用在储存的时候要进行类型判断。如果是用泛型
主要是数据结构中根本就没有必要天天关心中间value 是什么类型直接一开始就申请就不用做类型判断了
下面是泛型的总结
首先看一下kotlin中泛型的使用

class Box<T>(t: T) {
    var value = t
}
fun <T> singletonList(item: T): List<T> {
    // ……
}

声明泛型,然后在应用中进行实例化。
同样的在GO中要声明一个自定义的Array可以用到泛型
首先是
简单的用type 生成

type complexOne[T int|float64] T

但是不是很直观
这里可以用interface 的方式进行一组的泛型申请

type oneNodeValue interface {
	int | float64 | int8
}

然后就是复杂的类型申请
比如复杂的struct类型申请

type customizeArray[T oneNodeValue] struct {
	Name      string
	valueOne T
	ArrayData []T
}

像Kotlin一样要用的时候,前面指明泛型
[T 泛型的类型]
然后可以进行嵌套
但是嵌套后,相当于最底层的地方还是没有指明类型
需要每一个地方都指明用到什么
比如上面结构中

valueOne T
这里是指明了是T,然后就可以直接用

ArrayData []T
这里是一个T类型的切片,而且切片也没有进行申请
这里是列子,如果没有进行指明

var myArray = new(customizeArray[int])
	myArray.ArrayData[0] = 1
	fmt.Printf("%v %T", myArray.ArrayData[0], myArray.ArrayData[0])

这里会报错
panic: runtime error: index out of range [0] with length 0
没有进行指明和申请空间
当然这里是用make
接下来是泛型函数和泛型接受器
对只有这两个
没有泛型方法

func (receiver ) name() funcition {
	
}

方法不能有类型形参

func (receiver) name[T Generics]() funcition {

}

那么要实现方法的方式只能用
泛型接受器

func (c *customizeArray[oneNodeValue]) sum() oneNodeValue {
	var slice = c.ArrayData
	result := sun(slice)
	return result
}

通用的函数

func sun[T oneNodeValue](ArrayData []T) T {
	var result T
	for _, i2 := range ArrayData {
		result = result + i2
	}
	return result
}

初始化array,
如果用struct 生成的类型对象,每一个用了泛型的地方,都要去实例化

注意不要去想匿名的任何东西
匿名结构体不支持泛型,但是基本上我也不会用匿名结构体
匿名函数不支持泛型

所以用了泛型基本什么都老老实实的写
完整代码
//如果使用泛型,首先是創建問題

type oneNodeValue interface {
	int | float64 | int8
}

type customizeArray[T oneNodeValue] struct {
	Name      string
	valueOne  T
	ArrayData []T
}

//同样的方法也是一样的
func sun[T oneNodeValue](ArrayData []T) T {
	var result T
	for _, i2 := range ArrayData {
		result = result + i2
	}
	return result
}

func (c *customizeArray[oneNodeValue]) sum() oneNodeValue {
	var slice = c.ArrayData
	result := sun(slice)
	return result
}

//这里用法和kotlin 有点相似,你在实例化对象的时候要标注所有的类型

func main() {
	//初始化array,
	//如果用struct 生成的类型对象,每一个用了泛型的地方,都要去实例化

	//比如这里,先声明了这个是一个int
	//然后在里面有一个[]T 的切片
	//还有去结构体里面实例化

	//但是匿名结构体不支持泛型,但是基本上我也不会用匿名结构体
	//匿名函数不支持泛型
	var myArray = new(customizeArray[int])

	myArray.ArrayData = make([]int, 10)
	myArray.valueOne = 2
	myArray.ArrayData[0] = 1
	myArray.ArrayData[1] = 2

	var result = sun(myArray.ArrayData)
	fmt.Printf("%v %T\n", result, result)
	fmt.Println("-----------")
	var resultTwo = myArray.sum()
	fmt.Printf("%v %T %v\n", resultTwo, resultTwo, myArray.valueOne)

}

最后一点
在实现数据结构中,我尝试用泛型进行实现,代码精简

数据读取

1读取用户的输入

Unix 和 Windows 的行结束符是不同,要注意在unix上运行有什么不同
首先看一下go 中标准输出
各种常见的输出格式

var vFormat = "%v value"
var TFormat = "%T Type"
var tFormat = "%t bool" //输入 0 1 输出 false true
var dFormat = "%d int"
var fFormat = "%f flout"
var cFormat = "%c onebyte"
var sFormat = "%s string"
var pFoarmat = "%p pointer"

从键盘和标准输入 os.Stdin 读取输入
最简单的办法是使用 fmt 包提供的 Scan 和 Sscan 开头的函数

其中输入输出非常像C语言的一种写法

func fmtScanFunction() {
	//	非常像C语言的一种写法
	var scanfTest = new(value)
	var format = "%t/%d/%f/%s/%c"
	var b byte
	fmt.Scanf("%t %d %f %s %c", &scanfTest.t, &scanfTest.d, &scanfTest.f, &scanfTest.c, &b)
	fmt.Printf(format, scanfTest.t, scanfTest.d, scanfTest.f, scanfTest.c, b)
}

bufio 包提供的缓冲读取(buffered reader)来读取数据

func buffReadFunction() {
	var inputReader = bufio.NewReader(os.Stdin)
	fmt.Println("please input")
	var input, err = inputReader.ReadString('\n')
	if err == nil {
		fmt.Printf("input is %s", input)
	}
	//屏幕是标准输出 os.Stdout;os.Stderr
	//用于显示错误信息,大多数情况下等同于 os.Stdout
}

然后是练习
练习 12.1: word_letter_count.go

编写一个程序,从键盘读取输入。当用户输入 ‘S’ 的时候表示输入结束,这时程序输出 3 个数字:
i) 输入的字符的个数,包括空格,但不包括 ‘\r’ 和 ‘\n’
ii) 输入的单词的个数
iii) 输入的行数

这里学习一下go中的正则表达式

http://c.biancheng.net/view/5124.html

测试字符串

abc bder
a;ldf
pqet asadf
al  aa$

Unix系统里,每行结尾只有“<换行>”,即"\n";
Windows系统里面,每行结尾是“<回车><换行>”,即“\r\n”;
Mac系统里,每行结尾是“<回车>”,即"\r";。
一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,
所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话
,在每行的结尾可能会多出一个^M符号。

Go语言通过 regexp 包为正则表达式提供了官方支持,
其采用 RE2 语法,除了\c、\C外,Go语言和 Perl、Python 等语言的正则基本一致。
基本用法

//生成一个正则表达式
regexpOne := regexp.MustCompile("[<空格>\\t\\r\\n\\f\\v]")
//类型匹配,string或者byte 
byteMatchResult := regexpOne.Match(byteSlice)
//进行匹配
resultOne := regexpOne.FindAll(byteSlice, -1)
//-1是返回所有
func wordLetterCount() {
	var buffRead = bufio.NewReader(os.Stdin)
	var input, err = buffRead.ReadString('$')
	if err == nil {
		var byteSlice = []byte(input)
		var originalLength = len(byteSlice)
		
		regexpOne := regexp.MustCompile("[<空格>\\t\\r\\n\\f\\v]")
		
		regexpTwo := regexp.MustCompile("[A-Za-z0-9]{1,}")
		
		regexpThree := regexp.MustCompile("[\\n]")

		if regexpOne == nil {
			fmt.Println("regexp err")
			return
		}
		byteMatchResult := regexpOne.Match(byteSlice)
		fmt.Printf("%v", byteMatchResult)

		resultOne := regexpOne.FindAll(byteSlice, -1)
		resultTwo := regexpTwo.FindAll(byteSlice, -1)
		resultThree := regexpThree.FindAll(byteSlice, -1)

		fmt.Printf("%v %s", byteMatchResult, resultOne)
		var length = len(resultOne)
		fmt.Printf("字符个数为 %d\n", originalLength-length)
		for _, bytes := range resultTwo {
			for _, b := range bytes {
				fmt.Printf("%c", b)
			}
			fmt.Printf("\n")
		}
		fmt.Printf("单词个数%d", len(resultTwo))
		fmt.Printf("换行的个数%d", len(resultThree)+1)

	} else if err != nil {
		fmt.Println("input is failed")
	}
}

这里是输出
字符个数为 30
abc
bder
a
ldf
pqet
asadf
al
aa
单词个数8换行的个数4

2文件读写

Golang获取目录下的文件及目录信息
如果是获得所以目录信息是用递归的方法

var filePath = "D:\\goProject\\fileReadDirectory"

func fileDirectory(fP string) {
	var files, err = ioutil.ReadDir(fP)
	if err != nil {
		println(err)
		panic(err)
	}
	for i, file := range files {
		println(i, file.Name())
	}
}

func main() {
	fileDirectory(filePath)
}

各种文件的读写,
type Page struct {
Title string
Body []byte
}
因为这个结构可能相当巨大,我们不想在内存中拷贝它
所以一般都对文件名进行操作

对简单的字符串的读

这里只用注意两个部分
首先是要defer inputFile.Close()
还有就是分为两步
第一步 打开文件
然后 进行读取

func osOpen(name []string) {

	var path = name[1]
	var inputFile, inputError = os.Open(path)
	if inputError != nil {
		fmt.Printf("An error occurred on opening the inputfile\n" +
			"Does the file exist?\n" +
			"Have you got acces to it?\n")
		return // exit the function on error
	}

	defer inputFile.Close()

	var inputReader = bufio.NewReader(inputFile)
	//用缓冲池进行读取
	for {
		var inputString, readerError = inputReader.ReadString('\n')
		fmt.Printf("%s", inputString)
		if readerError == io.EOF {
			return
		}
	}
}
用字节流进行读取

这里用的包为ioutil 会重点看一下。因为后面还有多线程读写

func readFileWithByte(name []string) {
	var path = name[1]
	var buff, readError = ioutil.ReadFile(path)
	if readError != nil {
		fmt.Fprintf(os.Stderr, "file error %s\n", readError)
	}
	fmt.Printf("%s", buff)
}
用缓存池进行读取

因为很多时候,是图片文件etc
直接用file.read ()会方便很多
然后这里有一个问题
每次用这个必然会产生很多旧的切片
所以就有几个方法
byteSlice = append(byteSlice, buff…)

func noAppendFunction(buff []byte, byteSlice [][]byte, times int) (byteSliceNew [][]byte) {
	byteSlice[times] = make([]byte, 1024)
	copy(byteSlice[times], buff)
	return byteSlice
}
func readFileWithBuff(name []string) {
	var path = name[0]
	//首先还是打开文件
	var inputFile, inputError = os.Open(path)
	if inputError != nil {
		fmt.Printf("An error occurred on opening the inputfile\n" +
			"Does the file exist?\n" +
			"Have you got acces to it?\n")
		return // exit the function on error
	}
	defer inputFile.Close()

	//开缓冲池
	var buff = make([]byte, 1024)
	var byteSlice = make([]byte, 1024)
	var byteSliceTwo = make([][]byte, 1024)
	var times = 0
	for {
		//这里有个问题,buff 这样写每次也只写的到Buff多。这里还要一个储存的
		var n, err = inputFile.Read(buff)
		println(n, err)
		if n == 0 {
			break
		} else {
			byteSlice = append(byteSlice, buff...)
			byteSliceTwo = noAppendFunction(buff, byteSliceTwo, times)
			times = times + 1
		}
	}
	println(len(byteSlice), byteSlice)
	println(len(byteSliceTwo), byteSliceTwo, times)
}
用列表形式进行读取

不能对word excel 文件进行读取
用专门的包进行读写修改
baliance/gooxml 如果有需要去看这个文档

这里仅仅是一种形式的文本读取

func ReadDataFromFileByColumn(name []string) {
	var path = name[4]
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	var col1, col2, col3 []string
	for {
		var v1, v2, v3 string
		_, err := fmt.Fscanln(file, &v1, &v2, &v3)
		// scans until newline
		if err != nil {
			break
		}
		col1 = append(col1, v1)
		col2 = append(col2, v2)
		col3 = append(col3, v3)
	}

	fmt.Println(col1)
	fmt.Println(col2)
	fmt.Println(col3)
}
简单的读写
func outputFileWriter(name []string) {
	/*
		可以看到,OpenFile 函数有三个参数:文件名、
		一个或多个标志(使用逻辑运算符 “|” 连接),使用的文件权限。

		我们通常会用到以下标志:
		os.O_RDONLY:只读
		os.O_WRONLY:只写
		os.O_CREATE:创建:如果指定文件不存在,就创建该文件。
		os.O_TRUNC:截断:如果指定文件已存在,就将该文件的长度截为 0。

		在读文件的时候,文件的权限是被忽略的,所以在使用 OpenFile 时传入的第三个参数可以用 0。
		而在写文件时,不管是 Unix 还是 Windows,都需要使用 0666。
	*/
	//先打开文件,然后再写,写完关掉
	var pF = name[0]
	var outputFile, outputError = os.OpenFile(pF, os.O_WRONLY|os.O_CREATE, 0666)
	if outputError != nil {
		fmt.Printf("An error occurred with file opening or creation\n")
		return
	}
	defer outputFile.Close()
	// var outputWriter *bufio.Writer
	// var outputFile *os.File
	// var outputError os.Error
	// var outputString string

	var outputWriter = bufio.NewWriter(outputFile)
	var teststring = "this is ouu put string ,please try agein"
	var byteSlice = []byte(teststring)
	outputWriter.Write(byteSlice)
	outputWriter.Flush()
}

3文件拷贝

打开两个文件,然后都要关掉
注意defer顺序不要先把源文件关了
然后io包本身就有拷贝,自己也可以做差错检测

注意 defer 的使用:当打开目标文件时发生了错误,那么 defer 仍然能够确保 src.Close() 执行。如果不这么做,文件会一直保持打开状态并占用资源。

func CopyFile(destinationFileName string, original []string) (written int64, err error) {
	var path = original[1]
	
	var readFile, FileError = os.Open(path)
	if FileError != nil {
		return
	}
	defer readFile.Close()

	var destinationFile, FileErrorTwo = os.OpenFile(destinationFileName, os.O_WRONLY|os.O_CREATE, 0644)
	if FileErrorTwo != nil {
		return
	}
	
	defer destinationFile.Close()
	//	这里有Io复制的包,自己写就是buff流了
	return io.Copy(destinationFile, readFile)
}

4从命令行读取参数

用这个之前,在lunx中不拍,因为安装go要添加Path
如果在windos中一定要添加path

剩下的在网络传输中介绍
JSON 数据格式
XML 数据格式
用 Gob 传输数据
文件断点续传
Go 中的密码学
多线程中的数据读写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值