go的反射reflect和文件操作

1.反射

Go语言的变量分两部分,类型信息和值信息
在Go的反射机制中,任何接口值都是由一个具体类型和具体类型的值两部分组成
reflect.TypeOf和reflect.ValueOf两个重要的函数来获取任意对象的type和value

v:=reflect.TypeOf(x)
v.Name() // 类型名称
v.Kind() // 类型种类

指针类型的类型名称为空,类型种类为ptr,对象的类型种类是struct
数组类型的类型名称是空,类型种类是array
切片的类型名称是空,类型种类是slice

// 反射获取变量的原始值
v := reflect.ValueOf(x)
var m = v.Int() + 12


如果传入的是一个地址,想要修改值的话,就需要加一个Elem()
v.Elem().Kind() // 获取值

func printStruct(s interface{}) {
fmt.Println(reflect.TypeOf(s).Kind()) // 返回string
fmt.Println(reflect.ValueOf(s).Kind()) // 返回string
}
func main() {
a := "hello"
printStruct(a)
}
获取结构体字段:
type Student struct {
Name string `json:"name" form:"username"`
Age int `json:"age"`
Score int `json:"score"`
}
func main() {
stu := Student{
Name: "Hello",
Age: 3,
Score: 100,
}
t := reflect.TypeOf(stu)
v := reflect.ValueOf(stu)
//1.通过类型变量里面的Field可以获取结构体的字段,安装索引下标
field0 := t.Field(0)
fmt.Printf("%#v \n", field0)
fmt.Println("字段名称", field0.Name)
fmt.Println("字段类型", field0.Type)
fmt.Println("字段Tag:", field0.Tag.Get("json"))
fmt.Println("字段Tag:", field0.Tag.Get("form"))
// 2.通过类型变量里面的FieldByName可以获取结构体的字段,根据名称获取
field1, ok := t.FieldByName("Age")
if ok {
fmt.Printf("%#v \n", field1)
fmt.Println("字段名称", field1.Name)
fmt.Println("字段类型", field1.Type)
fmt.Println("字段Tag:", field1.Tag.Get("json"))
}
// 3.通过类型变量里面的NumField获取到该结构体有几个字段
fieldCount := t.NumField()
fmt.Println("结构体有多少个", fieldCount, "个属性")
// 4.通过值变量获取结构体属性对应的值
fmt.Println(v.FieldByName("Name"))
fmt.Println(v.FieldByName("Age"))
for i := 0; i < fieldCount; i++ {
fmt.Printf("属性名称:%v 属性值:%v 属性类型:%v 属性Tag:%v\n", t.Field(i).Name, v.Field(i), t.Field(i).Type,
t.Field(i).Tag.Get("json"))
}
}
获取结构体方法:
/*
reflect.Value.Method已经被废弃了。从Go 1.17开始,
它已被替换为reflect.Value.MethodByName。
建议使用新的方法以确保代码的兼容性和可移植性
*/
package main
import (
"fmt"
"reflect"
)
// func printStruct(s interface{}) {
// fmt.Println(reflect.TypeOf(s).Kind())
// fmt.Println(reflect.ValueOf(s).Kind())
// }
type Student struct {
Name string `json:"name" form:"username"`
Age int `json:"age"`
Score int `json:"score"`
}
func (s Student) SayHello() {
fmt.Println("123456")
}
func (s Student) SayWorld(addr string, time string) {
fmt.Println(addr + time)
}
func reflectChange(s interface{}) {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s) // 修改结构体的值
if t.Kind() != reflect.Ptr {
fmt.Println("传入的不是结构体指针类型")
return
} else if t.Elem().Kind() != reflect.Struct {
fmt.Println("传入的不是结构体指针类型")
return
}
// 修改结构体属性的值
name := v.Elem().FieldByName("Name")
name.SetString("小李")
age := v.Elem().FieldByName("Age")
age.SetInt(22)
}
func main() {
v := Student{
Name: "Hello",
Age: 3,
Score: 100,
}
a := reflect.ValueOf(v)
b := a.MethodByName("SayHello")
b.Call(nil)
// 4.执行方法传入参数(注意需要使用《值变量》,并且要注意参数,接收的参数是[]reflect.Value的切片)
var params []reflect.Value
params = append(params, reflect.ValueOf("新西兰"))
params = append(params, reflect.ValueOf("今年"))
a.MethodByName("SayWorld").Call(params) // 执行方法传入参数
// 5.获取方法数量
fmt.Println("方法数量", a.NumMethod())
// 6.修改反射体的属性
reflectChange(&v)
fmt.Printf("%#v\n", v)
}

反射是一个强大的工具,可以写出更灵活的代码,但是反射不应该被滥用
因为:

  • 基于反射的代码是极其脆弱,反射中的类型错误会在真正运行的时候才会引发panic,哪可能是在写完代码很长时间之后
  • 大量使用反射的代码通常难以理解

2.文件操作

文件读操作:
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 一、读取文件
// 法1:
// 只读方式打开当前目录下的main.go文件
//os.Open("D:/go_demo/demo01/main.go") // 绝对路径
file, err := os.Open("./main.go") // 想对路径
defer file.Close() //文件打开之后必须关闭
if err != nil {
fmt.Println(err)
return
}
// 读取文件里面的内容
var strSlice []byte
var tempSlice = make([]byte, 128)
for {
n, err := file.Read(tempSlice)
if err == io.EOF {
fmt.Println("读取完毕")
break
}
if err != nil {
fmt.Println("读取失败")
return
}
//fmt.Printf("读取到了%v个字节", n)
//做为形参的参数前的三个点意思是可以传0到多个参数,变量后三个点意思是将一个切片或数组变成一个一个的元素,俗称将数组打散
// 因为是byte切片,所以想看到的话,需要先转换成字符串
strSlice = append(strSlice, tempSlice[:n]...) //注意这个写法
fmt.Println(string(tempSlice))
}
// 法2:bufio 读取文件
file, err = os.Open("./main.go") // 想对路径
defer file.Close() //文件打开之后必须关闭
if err != nil {
fmt.Println(err)
return
}
// bufio读取文件
var fileStr string
reader := bufio.NewReader(file)
for {
str, err := reader.ReadString('\n') // 表示一次读取一行,注意单引号
if err == io.EOF {
fileStr += str // 如果有空行会出错,不完整,所以加上这一条
break
}
if err != nil {
fmt.Println(err)
return
}
fmt.Println(str)
fileStr += str
}
fmt.Println(fileStr)
// 法3:ioutil 读取文件
// 上述两种都是通过流读取,第三种是一次性读取
// 但是这种方法在最新版本的go里面已经废除了
}
文件写操作:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
// 写入文件
// 一、写入文件
// 法1:
// 1、打开文件 file,err := os.OpemFile("",os.O_CREATE|os.O_RDWR,0666)
file, err := os.OpenFile("./test.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) // 第二个参数表示模式,最后一个参数表示在linux下的权限
defer file.Close()
if err != nil {
fmt.Println(err)
return
}
// 写入文件
for i := 0; i < 10; i++ {
file.WriteString("直接写入的字符串数据" + strconv.Itoa(i) + "\r\n") // 在记事本里面只写\n不能识别到,必须写\r\n
}
// file.Write写入,但是需要传入切片
var str = "直接写入byte"
file.Write([]byte(str))
// 法2:通过 bufio写入
Writer := bufio.NewWriter(file)
for i := 0; i < 10; i++ {
Writer.WriteString("你好golang") // 在记事本里面只写\n不能识别到,必须写\r\n
}
Writer.Flush() // 把缓存数据写入文件
}
文件复制、删除、重命名操作:
package main
import (
"fmt"
"io"
"os"
)
func CopyFile(srcFileName string, dstFileName string) (err error) {
sFile, err1 := os.Open(srcFileName)
defer sFile.Close()
dFile, err2 := os.OpenFile(dstFileName, os.O_CREATE|os.O_WRONLY, 0666)
defer dFile.Close()
if err1 != nil {
return err1
}
if err2 != nil {
return err2
}
var tempSlice = make([]byte, 128)
for {
// 读取数据
n1, e1 := sFile.Read(tempSlice)
if err == io.EOF {
break
}
if e1 != nil {
return e1
}
// 写入数据
_, e2 := dFile.Write(tempSlice[:n1])
if e2 != nil {
return e2
}
}
return nil
}
func main() {
// 以文件流的方式复制文件
srcFile := "./test.txt"
dstFile := "./target.txt"
err := CopyFile(srcFile, dstFile)
if err != nil {
fmt.Printf("拷贝完成\n")
} else {
fmt.Printf("拷贝错误 err=%v\n", err)
}
// 创建目录
err1 := os.Mkdir("./abc", 0666)
//err1 := os.MkdirAll("./abc/def/jkl", 0666) // 也可以创建多层目录
if err1 != nil {
fmt.Println(err)
}
// 删除文件和目录Remove可以删除文件也可以删除目录
//err = os.Remove("target.txt")
/*
err = os.Remove("./abc") // 删除目录
if err != nil {
fmt.Println(err)
}
// 一次删除多个文件removeAll,包括这个目录下的所有文件
err = os.RemoveAll("./abc") // 删除目录
if err != nil {
fmt.Println(err)
}
*/
// 重命名rename
err = os.Rename("./test.txt", "./hello.txt")
if err != nil {
fmt.Println(err)
}
fmt.Println("重命名成功")
}

本文作者:oaoa

本文链接:https://www.cnblogs.com/oaoa/p/17299750.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   喵喵队立大功  阅读(82)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示