传统的数据类型断言
在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 中的密码学
多线程中的数据读写