南京网站设计公司兴田德润放心,设置备份管理wordpress,756ka网站建设,什么是网站什么是网站建设反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一#xff1a;使用Read()读取文件2.3、方式二#xff1a;bufio读取文件2.4、方式三#xff1a;os.ReadFile读取2.5、写… 反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一使用Read()读取文件2.3、方式二bufio读取文件2.4、方式三os.ReadFile读取2.5、写入文件2.6、复制文件2.7、文件重命名2.8、创建目录2.9、删除目录和文件 1、反射
有时我们需要写一个函数这个函数有能力统一处理各种值类型而这些类型可能无法共享同一个接口 也可能布局未知也有可能这个类型在我们设计函数时还不存在这个时候我们就可以用到反射。
空接口可以存储任意类型的变量那我们如何知道这个空接口保存数据的类型是什么值是什么呢 1、可以使用类型断言 2、可以使用反射实现 也就是在程序运行时动态的获取一个变量的类型信息和值信息。
Golang 中反射可以实现以下功能 1、反射可以在程序运行期间动态的获取变量的各种信息比如变量的类型、类别 2、如果是结构体通过反射还可以获取结构体本身的信息比如结构体的字段、结构体的方法、结构体的tag。 3、通过反射可以修改变量的值可以调用关联的方法。 Go 语言中的变量是分为两部分的: • 类型信息预先定义好的元信息。 • 值信息程序运行过程中可动态变化的。 在GoLang的反射机制中任何接口值都由是一个具体类型和具体类型的值两部分组成的。在GoLang中反射的相关功能由内置的reflect包提供任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成并且reflect包提供了reflect.TypeOf和reflect.ValueOf两个重要函数来获取任意对象的Value和Type。
1.1、reflect.TypeOf()获取任意值的类型对象
1、使用reflect.TypeOf可以获取函数类型对象。
package mainimport (fmtreflect
)type myInt inttype Person struct {Name stringAge int
}// 通过反射获取任意变量的类型
func reflectFn(x interface{}) {v : reflect.TypeOf(x)fmt.Println(v)
}func main() {reflectFn(10)var a 10reflectFn(3.1415)reflectFn(true)reflectFn(你好golang)reflectFn([]int{1, 2, 3, 4, 5})reflectFn(a)p : Person{Name: 张三,Age: 18,}reflectFn(p)var x myInt 10reflectFn(x)
}2、通过reflect.TypeOf获取返回的类型对象后该对象里面还有两个方法Name和Kind。Name用来获取类型名称Kind用来获取类型种类。
package mainimport (fmtreflect
)type myInt inttype Person struct {Name stringAge int
}func reflectFn(x interface{}) {v : reflect.TypeOf(x)fmt.Printf(类型: %v, 类型名称: %v, 类型种类: %v\n, v, v.Name(), v.Kind())
}func main() {var a 10reflectFn(10)reflectFn(3.1415)reflectFn(true)reflectFn(你好golang)reflectFn([]int{1, 2, 3, 4, 5})reflectFn(a)p : Person{Name: 张三,Age: 18,}reflectFn(p)var x myInt 10reflectFn(x)
}Go语言的反射中像数组、切片、Map、指针等类型的变量它们的.Name()都是返回空。种类Kind就是指底层的类型。 1.2、reflect.ValueOf()
reflect.ValueOf()返回的是 reflect.Value 类型其中包含了原始值的值信息。reflect.Value 与原始值之间可以互相转换。
1、例如实现接受任意值的接口提取出原始值进行运算操作。
package mainimport (fmtreflect
)func reflectFn(x interface{}) {v : reflect.ValueOf(x)num : v.Int() 10fmt.Println(num)
}func main() {reflectFn(10)
}在上面的代码中我们先获取reflec.Value对象然后通过该对象获取原始值进行运算操作。
2、但是上面这种情况是在我们知道类型的情况下调用对应的获取原始值方法 如果有多种类型我们就需要进行判断。我们可以使用reflec.Value对象提供的kind函数获取种类然后根据种类进行判断再调用对应的获取原始值方法。
package mainimport (fmtreflect
)func reflectFn(x interface{}) {v : reflect.ValueOf(x)kind : v.Kind()switch kind {case reflect.Int:fmt.Printf(int类型的原始值: %v\n, v.Int())case reflect.Bool:fmt.Printf(bool类型的原始值: %v\n, v.Bool())case reflect.Float64:fmt.Printf(float64类型的原始值: %v\n, v.Float())case reflect.String:fmt.Printf(string类型的原始值: %v\n, v.String())default:fmt.Println(还没有判断这个类型...)}
}func main() {reflectFn(10)reflectFn(true)reflectFn(3.1415)reflectFn(你好golang)
}3、在反射中修改变量的值。 想要在函数中通过反射修改变量的值 需要注意函数参数传递的是值拷贝 必须传递变量地址才能修改变量值。而反射中使用专有的 Elem()方法来获取指针对应的值。
package mainimport (fmtreflect
)func reflectFn(x interface{}) {v : reflect.ValueOf(x)fmt.Println(v, v.Kind(), v.Elem(), v.Elem().Kind())
}func main() {var x 10reflectFn(x)
}由于我们传入的是一个指针所以获取值对象打印输出就是一个地址通过kind获取类型是ptr我们可以通过Elem函数来获取该指针指向的值而不能通过解引用的方式来直接获取。要修改对应的值就先使用.Elem获取对象然后再调用SetXXX来修改。
package mainimport (fmtreflect
)func reflectFn(x interface{}) {v : reflect.ValueOf(x)if v.Elem().Kind() reflect.Int {v.Elem().SetInt(20)} else if v.Elem().Kind() reflect.String {v.Elem().SetString(hello c)}
}func main() {var x 10var str hello golangfmt.Println(x, str)reflectFn(x)reflectFn(str)fmt.Println(x, str)
}1.3、结构体反射
任意值通过reflect.TypeOf()获得反射对象信息后如果它的类型是结构体可以通过反射值对象reflect.Type的NumField()和Field()方法获得结构体成员的详细信息。
1、通过类型变量的Field方法获取结构体字段。
package mainimport (fmtreflect
)type Student struct {Name string json:name1 form:uernameAge int json:ageScore int json:score
}func (s Student) GetInfo() {fmt.Printf(姓名: %v, 年龄: %v, 成绩: %v\n, s.Name, s.Age, s.Score)
}func (s *Student) SetInfo(name string, age int, score int) {s.Name names.Age ages.Score score
}func PrintStructField(s Student) {t : reflect.TypeOf(s)// v : reflect.ValueOf(s)if t.Kind() ! reflect.Struct t.Elem().Kind() ! reflect.Struct {fmt.Println(传入的不是一个结构体类型...)return}// 1.通过类型变量的Field方法获取结构体字段field0 : t.Field(0)fmt.Printf(%#v\n, field0)
}func main() {stu : Student{Name: 张三,Age: 18,Score: 99,}PrintStructField(stu)
}返回的是一个reflect.StructField对象我们打印出看看里面有什么内容。 我们可以通过该结构体对象的Name、Type、Tag方法来获取对应的信息
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, _ : t.FieldByName(Age)
fmt.Println(字段名称:, field1.Name)
fmt.Println(字段类型:, field1.Type)
fmt.Println(字段Tag:, field1.Tag.Get(json))3、通过类型变量的NumField方法可以获取到该结构体里有几个字段。
var fieldCount t.NumField()
fmt.Println(该结构体字段数量:, fieldCount)4、通过值变量获取结构体里面对应的值。
v : reflect.ValueOf(s)
fmt.Printf(Name的值: %v, Age的值: %v\n, v.Field(0), v.FieldByName(Age))5、通过类型变量里面的Method方法获取结构体中的方法。
func PrintStructFn(x interface{}) {t : reflect.TypeOf(x)if t.Kind() ! reflect.Struct t.Elem().Kind() ! reflect.Struct {fmt.Println(传入的参数不是一个结构体...)return}method0 : t.Method(0)fmt.Println(方法名称:, method0.Name)fmt.Println(方法类型:, method0.Type)
}这里传入0获取方法并不是按照方法的定义顺序来获取的而是通过字典序来获取的。
6、通过类型变量里面的MethodByName获取方法。
method1, _ : t.MethodByName(GetInfo)
fmt.Println(方法名称:, method1.Name)
fmt.Println(方法类型:, method1.Type)也可以通过NumMethod获取该结构体中有多少个方法。
7、通过值变量来执行方法。
v : reflect.ValueOf(x)
v.MethodByName(GetInfo).Call(nil)上面调用GetInfo方法不需要传参Call里面填写nil即可如果需要传参需要定义一个reflect.Value切片传入如下
fmt.Println(x)
var params []reflect.Value
params append(params, reflect.ValueOf(李四))
params append(params, reflect.ValueOf(22))
params append(params, reflect.ValueOf(100))
v.MethodByName(SetInfo).Call(params)
fmt.Println(x)8、反射修改结构体属性。
package mainimport (fmtreflect
)type Student struct {Name string json:nameAge int json:ageScore int json:score
}func reflectChange(x interface{}) {t : reflect.TypeOf(x)v : reflect.ValueOf(x)if t.Kind() ! reflect.Ptr {fmt.Println(传入的不是指针类型...)}if t.Elem().Kind() ! reflect.Struct {fmt.Println(传入的不是结构体指针类型...)}name : v.Elem().FieldByName(Name)name.SetString(李四)age : v.Elem().FieldByName(Age)age.SetInt(22)
}func main() {stu : Student{Name: 张三,Age: 18,Score: 100,}fmt.Println(stu)reflectChange(stu)fmt.Println(stu)
}2、文件操作
2.1、os.Open()打开文件
打开文件使用os.Open函数传入文件的地址可以采用绝对地址也可以采用相对地址。记得调用Close函数关闭文件。
package mainimport (fmtos
)func main() {file, err : os.Open(./main.go)defer file.Close()if err ! nil {fmt.Println(err:, err)return}
}这种打开方式默认是只读的。 2.2、方式一使用Read()读取文件 调用Read函数需要传入一个byte切片类型用来存储数据。该函数有两个返回值第一个返回值表示读取的字节数。
package mainimport (fmtioos
)func main() {file, err : os.Open(./main.go)defer file.Close()if err ! nil {fmt.Println(err:, err)return}var str []bytetmp : make([]byte, 128)for {n, err : file.Read(tmp)if err io.EOF {fmt.Println(读取完毕...)break}if err ! nil {fmt.Println(err:, err)return}str append(str, tmp[:n]...)}fmt.Println(string(str))
}注意 1、每次只能读取128个字节所以我们不知道什么时候读取完毕通过对err io.EOF来判断是否读取完毕。 2、切片是饮用类型我们在拼接两个切片的时候要指明tmp的区间否则可能会把之前残留的数据也拼接到str中。 2.3、方式二bufio读取文件
package mainimport (bufiofmtioos
)func main() {file, err : os.Open(./main.go)defer file.Close()if err ! nil {fmt.Println(err)return}var fileStr stringreader : bufio.NewReader(file)for {str, err : reader.ReadString(\n) // 表示一次读取一行if err io.EOF {fileStr strbreak}if err ! nil {fmt.Println(err)return}fileStr str}fmt.Println(fileStr)
}2.4、方式三os.ReadFile读取
package mainimport (fmtos
)func main() {byteStr, err : os.ReadFile(./main.go)if err ! nil {fmt.Println(err)return}fmt.Println(string(byteStr))
}2.5、写入文件
写入文件就不能使用os.Open()打开了这样是只读的需要使用os.OpenFile()函数。
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
...
}name要打开的文件名。flag打开文件的模式。模式有以下几种 perm表示文件的权限如果文件不存在我们创建文件的权限传入0666即可。
下面演示三种写入文件的方式 1、Write和WriteString写入。
package mainimport (fmtos
)func main() {file, err : os.OpenFile(./file.txt, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)defer file.Close()if err ! nil {fmt.Println(err)return}for i : 0; i 10; i {file.WriteString(fmt.Sprintf(我是一行字符串-%d\n, i))}var str 哈哈哈哈哈file.Write([]byte(str))
}2、bufio.NewWriter配合Flush写入。
package mainimport (bufiofmtos
)func main() {file, err : os.OpenFile(./file.txt, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)defer file.Close()if err ! nil {fmt.Println(err)}writer : bufio.NewWriter(file)for i : 1; i 10; i {writer.WriteString(fmt.Sprintf(我是一行字符串-%d\n, i))}writer.Flush()
}这里写入的是缓存中我们还需要调用Flush刷新才行。
3、os.WriteFile写入。
package mainimport (fmtos
)func main() {str : hello worlderr : os.WriteFile(./file.txt, []byte(str), 0666)if err ! nil {fmt.Println(err)}
}注意这种写入的方式每次都会清空文件内容所以如果要追加写入还是得采用前两种方式。 2.6、复制文件
实现方式一通过os.ReadFile从文件中读取所有内容再通过os.WriteFile写入到另一个文件中。
package mainimport (fmtos
)func copy(srcFileName string, dstFileName string) error {byteStr, err : os.ReadFile(srcFileName)if err ! nil {return err}err os.WriteFile(dstFileName, byteStr, 0666)if err ! nil {return err}return nil
}func main() {src : ./file1.txtdst : ./file2.txterr : copy(src, dst)if err ! nil {fmt.Println(err)return}fmt.Println(复制文件成功)
}方式二方法流的方式复制。
package mainimport (fmtioos
)func copy(srcFileName string, dstFileName string) error {src, err : os.Open(srcFileName)defer src.Close()if err ! nil {return err}dst, err : os.OpenFile(dstFileName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)defer dst.Close()if err ! nil {return err}var byteStr make([]byte, 128)for {n, err : src.Read(byteStr)if err io.EOF {break}if err ! nil {return err}if _, err : dst.Write(byteStr[:n]); err ! nil {return err}}return nil
}func main() {src : ./file1.txtdst : ./file2.txterr : copy(src, dst)if err ! nil {fmt.Println(err)}fmt.Println(复制文件完成...)
}2.7、文件重命名
使用os.Rename函数对文件进行重命名。
package mainimport (fmtos
)func main() {err : os.Rename(./file1.txt, ./file2.txt)if err ! nil {fmt.Println(err)return}fmt.Println(重命名成功...)
}2.8、创建目录
使用os.Mkdir创建目录使用os.MkdirAll创建多级目录。
package mainimport (fmtos
)func main() {err : os.Mkdir(./abc, 0666)if err ! nil {fmt.Println(err)return}fmt.Println(创建目录成功...)err os.MkdirAll(./d1/d2/d3, 0666)if err ! nil {fmt.Println(err)return}fmt.Println(创建多级目录成功...)
}2.9、删除目录和文件
使用os.Remove删除目录或文件使用os.RemoveAll删除多个文件或目录。
package mainimport (fmtos
)func main() {err : os.Remove(./file1.txt)if err ! nil {fmt.Println(err)return}err os.Remove(./d1)if err ! nil {fmt.Println(err)return}err os.RemoveAll(./d2)if err ! nil {fmt.Println(err)return}fmt.Println(删除成功...)
}