Day05 Go语言文件操作,结构体,构造函数,方法接收器,json序列化
Posted 澐湮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day05 Go语言文件操作,结构体,构造函数,方法接收器,json序列化相关的知识,希望对你有一定的参考价值。
01上节回顾
今日重点:结构体和文件操作
函数回顾
函数:一种代码组织形式
打印菱形,解决代码冗余问题,可维护性
package main import ( "fmt" "reflect" ) func foo() string fmt.Println("foo...") return "foo" func main() //[3]int [4]int 长度为3的数组类型,长度为4的数组类型,不一样 var x=100 fmt.Println(x) fmt.Println(foo,reflect.TypeOf(foo)) //0xc6c560 打印出来一个地址 foo() bar:=foo //加括号,返回值赋给bar。不加括号把函数拷贝给bar bar() //存储的相同的内容,不是一块儿内存空间
上节核心考点值传递
参数回顾
示例1 参数
package main import ( "fmt" "reflect" ) func add(x,y int) int return x+y func main() //参数 fmt.Println(add(1,5))
示例2 作用域
package main import "fmt" func add(a,b int) int a=100 return a+b func main() //参数 var a,b=1,2 fmt.Println(add(a,b)) // 102 fmt.Println(a,b) // a和b还是1和2 注意作用域问题
指针类型回顾
取地址能做什么?
指针声明和赋值
package main import ( "fmt" "reflect" ) func main() var x = "hello" //var p =&x var p *string p=&x //取址 fmt.Println(p,reflect.TypeOf(p)) //取值 *指针变量 fmt.Println(*p) //hello
指针应用
指针应用1 没有使用指针时
package main import ( "fmt" "reflect" ) func bar(t int) t=100 func main() //指针应用 var t = 1 bar(t) fmt.Println(t) 打印结果还是1
指针应用2 打印出地址
package main import ( "fmt" ) func bar(p *int) *p=100 fmt.Println("bar函数p地址",p) func main() //指针应用2 var t=1 var p =&t fmt.Println("main p地址",p) bar(p)
图解
通过指向同一地址空间,实现修改
指针应用3 修改指针对应的值
package main import ( "fmt" ) func bar(p *int) *p=100 fmt.Println("bar函数p地址",p) func main() //指针应用 var t=1 var p =&t fmt.Println("main p地址",p) bar(p) fmt.Println("main t",t)
02 上节回顾2
匿名函数
匿名函数可以在使用函数的时候声明调用
package main import "fmt" func main() //(1) (func() fmt.Println("yuan") )() //(2) var z=(func(x,y int)int return x+y )(1,2) fmt.Println(z)
高阶函数
2个点
函数作为参数
函数作为返回值
函数作为参数
值拷贝,存的函数,拷贝函数,赋给f
package main func test02() func test01(f func()) func main() test01(test02)
函数作为返回值
outer为高阶函数
package main func outer() func() return func()
闭包
流程判断相关:循环、 if else 是否开辟作用域?? 开辟了作用域
package main import "fmt" func main() if true var x=100 fmt.Println(x) fmt.Println(x) //报错,作用域问题
一个内部的函数,引用了外部的自由变量
闭包函数一调用,返回匿名函数
不是闭包函数
没有引用外部非全局变量
//不是闭包函数 func biBao() func() var ret=func() var x=10 fmt.Println(x) return ret func main() f:=biBao() f()
闭包函数
被引用的x,被保留下来了。
package main import "fmt" func biBao() func() //闭包 var x=10 var ret=func() fmt.Println(x) return ret
闭包函数2 地址空间
var x=10 和x=100 用的是一块地址空间
package main import "fmt" func biBao() func() //不是闭包函数 var x=10 fmt.Println(&x) var ret=func() x=100 fmt.Println(":::",&x) return ret func main() f:=biBao() f() 打印结果 0xc00000a0b8 ::: 0xc00000a0b8
内部使用x:=100,var x=10 和x:=100 用的不是一块地址
此时仍然是闭包函数,&x引用了外部自由变量
package main import "fmt" func biBao() func() //不是闭包函数 var x=10 fmt.Println(&x) var ret=func() fmt.Println(&x) x:=100 fmt.Println(":::",&x) return ret func main() f:=biBao() f() 打印结果 0xc000102058 0xc000102058 ::: 0xc000102090
作用域问题
x地址一样
func test2() var x=10 fmt.Println(&x) if true //if开辟作用域,也用了外部的变量 fmt.Println(x)
x地址不一样,又开辟了空间
func test() var x=10 fmt.Println(&x) if true //if开辟作用域,也用了外部的变量 var x=1 fmt.Println(x)
defer语句回顾
遇到return执行defer问题
03 编码小历史
编码
程序部分和存储部分分离
存储结构:内存和外存
CPU 运算器和控制器
输入和输出设备
高低电频
汇编语言:引入英文字母,极大提高性能。
编码系统:字母和二进制数字对应起来
ASCII表:最原始的编码表
利用多大空间存储:
bit位
8个bit位1字节 存256个二进制数字
ASCII表 用来127位
ASC扩展: 拉美使用了扩展位
0 000 0000
0 48 A 65 a 97
GBK- 用2个字节 65536个
0000 0000 0000 0000
---- ---- ---- ----
Unicode编码:一个符号4字节
0000 0000 0000 0000 0000 0000 0000 0000
UTF8编码(可伸缩的编码方式)
针对ASCII就用一个字节
针对汉字使用三个字节
package main import "fmt" func main() var a=\'0\' fmt.Println(a) var b = 48 fmt.Printf("%c",b) 打印结果 48 0
04 Go的字符和字节
Go的字符与字节
byte就是字节,一个字节就是8个二进制位。uint8无符号整型,
byte和uint8本质没区别,都是ASCII码表的一个字符
package main import "fmt" func main() var x uint8 x=255 fmt.Println(x) //255 var b byte b=\'A\' fmt.Println(b) //65 fmt.Printf("%c:%d\\n",b,b) //A:65
run占用4字节,32bit位,run本质和int32没区别,表示一个Unicode字符
package main import "fmt" func main() var r rune r=\'a\' fmt.Printf("%c:%d\\n",r,r) //a:97 r=\'中\' fmt.Printf("%c:%d\\n",r,r) //中:20013
字符串和字节串转换
编码与解码
package main import ( "fmt" "reflect" ) func main() //字符串与字节串之间的转换 var s="沙河" fmt.Println(s,reflect.TypeOf(s)) var b = []byte(s) fmt.Println(b,reflect.TypeOf(b)) //3个字节对应一个汉字 [230 178 153 230 178 179] []uint8 fmt.Println(string(b))
编码和解码
func main() //编码 var s = "沙河" b:=[]byte(s) fmt.Println(b,reflect.TypeOf(b)) //解码 fmt.Println(string(b))
下午 05 字符串
遍历字符串
三要素for循环
func main() //遍历字符串 //方式1 索引方式(会出现乱码) var hi= "hello 你好" for i:=0;i<len(hi);i++ //fmt.Println(string(hi[i])) fmt.Printf("%c",hi[i])
range
//遍历字符串 //方式2 用range方式遍历 for i,v:=range hi fmt.Println(i,string(v))
go语言中的字符串字符不可以修改
//go语言中的字符串字符不可以修改 fmt.Println(hi[0]) hi[0]=\'i\'
下午06 读文件
读文件:读到内存
写文件: 从内存写入
打开文件
var data = make([]byte,3) fmt.Println("data init",data) //data init [0 0 0]
首字母大写表示公开的,小写表示私有的
package main import ( "fmt" "os" ) func main() //打开文件 var file,err=os.Open("天道") //报错处理 if err != nil fmt.Println("err",err) //延迟注册 defer file.Close() //读文件数据 var data = make([]byte,3) fmt.Println("data init",data) //data init [0 0 0] n,err:=file.Read(data) if err != nil fmt.Println("err::",err) fmt.Println("n:",n) //n: 3
1 按字节读文件(使用不多)
package main import ( "fmt" "os" ) func readBytes(file *os.File) var data = make([]byte,3) fmt.Println("data init",data) n,err:=file.Read(data) if err != nil fmt.Println("err::",err) fmt.Println(n) fmt.Println("data:",string(data)) func main() //打开文件 var file,err=os.Open("天道") //报错处理 if err != nil fmt.Println("err",err) //延迟注册 defer file.Close() //按字节读文件数据 readBytes(file)
2 按字符串读
示例1
分隔符,字节类型
打印换行问题
原内容有换行,println又增加了一个换行,使用print fmt.Print(line)
package main import ( "bufio" "fmt" "io" "os" ) func main() //打开文件 var file, err = os.Open("天道") //报错处理 if err != nil fmt.Println("err", err) //延迟注册 defer file.Close() //(2)按字符串 reader:=bufio.NewReader(file) for true line,err:=reader.ReadString(\'\\n\') fmt.Print(line) if err==io.EOF break
示例2 放入函数中
package main import ( "bufio" "fmt" "io" "os" ) func readString(file *os.File) reader:=bufio.NewReader(file) for true line,err:=reader.ReadString(\'\\n\') fmt.Print(line) if err==io.EOF break func main() //打开文件 var file, err = os.Open("天道") //报错处理 if err != nil fmt.Println("err", err) //延迟注册 defer file.Close() //(2)按字符串 readString(file)
3 读取整个文件
package main import ( "fmt" "io/ioutil" "os" ) func main() //打开文件 var file, err = os.Open("天道") //报错处理 if err != nil fmt.Println("err", err) //延迟注册 defer file.Close() //(3)读取整个文件 content,err:=ioutil.ReadFile("天道") fmt.Println(string(content))
注意:文件中有空行符就都会打印
下午07 写文件
1 写字节
打开文件OpenFile
package main import ( "fmt" "os" ) func main() //文件名 模式 权限 file,err:=os.OpenFile("天道2",os.O_CREATE|os.O_WRONLY|os.O_TRUNC,0666) if err != nil fmt.Println("open file failed,err",err) defer file.Close() //写字节 var s="悟道休言天命2" //file.Write([]byte(s)) //写字节 file.WriteString(s)
2 写字符串
package main import ( "bufio" "fmt" "os" ) func main() //文件名 模式 权限 file,err:=os.OpenFile("天道2",os.O_CREATE|os.O_WRONLY|os.O_TRUNC,0666) if err != nil fmt.Println("open file failed,err",err) defer file.Close() //(2)写字符串 writer:=bufio.NewWriter(file) writer.WriteString("修行勿取真经。") //更新磁盘 writer.Flush()
3写文件
WriteFile是覆盖,文件不存在时,会创建
package main import ( "io/ioutil" ) func main() //(3) s:=` 悟道休言天命 修行勿取真经` ioutil.WriteFile("天道3",[]byte(s),0666)
下午08 声明结构体并实例化
声明结构体
格式
字段名必须唯一,可以通过逗号隔开
type 类型名 struct //标识结构体的类型名,在同一个包内不能重复 字段1 字段1类型 //字段名必须唯一 字段2 字段2类型
同类型的变量也可以写在一行,用逗号隔开
type Book struct title,author string price int
示例
//声明一个结构体 type Student struct name string //结构体的成员变量 age int score map[string]int
实例结构体对象
结构体和结构体的实例对象
先有对象,具有相同属性和行为的对象,称为类。
有这些属性的个体,称为对象。
方式1 先声明,再赋值
package main import "fmt" //声明一个结构体 type Student struct name string //结构体的成员变量 age int score map[string]int func main() //实例结构体对象 //方式1 直接声明 先声明,再赋值 var s Student //结构体是值类型 fmt.Println("s",s) //s 0 map[]
图示:开辟内存空间
赋值
package main import "fmt" //声明一个结构体 type Student struct name string //结构体的成员变量 age int score map[string]int func main() //实例结构体对象,支持.操作 //方式1 先声明,再赋值 var s Student //结构体是值类型 fmt.Println("s", s) //s 0 map[] s.name = "zhangsan" s.age = 22 s.score = map[string]int"语文": 95, "数学": 98, "英语": 93 fmt.Println(s.age) //22 fmt.Println(s) //zhangsan 22 map[数学:98 英语:93 语文:95]
下午 09 结构体实例化方式
方式2 声明并赋值(键值对赋值)
package main import "fmt" //声明一个结构体 type Student struct name string age int score map[string]int func main() //实例结构体对象,支持.操作 //方式2 声明并赋值 var s2 Student s2 = Studentname: "rain", age: 32, score: map[string]int"语文": 100, "数学": 90, "英语": 80 fmt.Println(s2) //rain 32 map[数学:90 英语:80 语文:100] fmt.Println(s2.age) //32 fmt.Println(s2.score["数学"]) //90
方式3 声明并赋值(多值赋值)
//方式3 声明并赋值 多值赋值 s3:=Student"alvin",18, map[string]int"yuwen":100,"shuxue":90,"yingyu":89 fmt.Println(s3)
s3的地址和s3.name的地址一样吗?
一样
package main import "fmt" //声明一个结构体 type Student struct name string age int score map[string]int func main() //实例结构体对象,支持.操作 //方式3 声明并赋值 多值赋值 s3:=Student"alvin",18, map[string]int"yuwen":100,"shuxue":90,"yingyu":89 fmt.Println(s3) fmt.Printf("%p,%p\\n",&s3,&s3.name) //0xc0000444a0,0xc0000444a0
练习中的思考
练习补充1:结构体中切片元素添加
对学生管理结构体中添加学生成绩
package main import "fmt" type Student struct Name string //结构体成员变量 Age int Score [3]int type StudentManager struct AllStu []Student func main() smm:=StudentManager[]Student var stu1 Student=Student"张三",18,[3]int90,98,97 stu2:=Student"李四",19,[3]int91,92,93 stu3:=Student"zhngsan",20,[3]int99,98,95 smm.AllStu=append(smm.AllStu,stu1) smm.AllStu=append(smm.AllStu,stu2) smm.AllStu=append(smm.AllStu,stu3) fmt.Println(smm) 打印结果 [张三 18 [90 98 97] 李四 19 [91 92 93] zhngsan 20 [99 98 95]]
练习补充2:结构体中map对象添加元素(开辟空间)
package main import "fmt" type Student struct Name string Age int score [3]int type StuManager struct Id int allStu map[string]Student func main() smm:=StuManager //需要先开辟空间,否则报错 smm.allStu=make(map[string]Student) fmt.Println(smm) stu1:=Student"zhangsan",19,[3]int90,98,97 smm.Id=1001 smm.allStu["1001"]=stu1 stu2:=Student"lisi",20,[3]int80,90,95 //注意:Id是值元素,再赋值时覆盖 smm.Id=1002 //map元素 不存在时添加,存在时修改 smm.allStu["1002"]=stu2 //stu3:=Student"lisi",20,[3]int80,90,95 //smm.allStu["1001"]=stu3 //会覆盖map键对应"1001"的值 fmt.Println(smm) //1002 map[1001:zhangsan 19 [90 98 97] 1002:lisi 20 [80 90 95]]
练习补充3:结构体中map对象添加元素2(开辟空间)
有两个学生对象,添加到结构体中(结构体中对象是map类型)
package main import "fmt" type Student struct Name string Age int score [3]int type StuManager struct allStu map[string]Student func main() smm:=StuManager /* //需要先开辟空间,否则报错 smm.allStu=make(map[string]Student) fmt.Println(smm)*/ stu1:=Student"zhangsan",19,[3]int90,98,97 stu2:=Student"lisi",20,[3]int80,90,95 //smm.allStu["1001"]=stu1 s1:=map[string]Student"1001":stu1 s2:=map[string]Student"1002":stu2 //当有两个学生对象,如何加入结构体?还是需要先开辟空间,再遍历 /* //这种会导致覆盖 smm.allStu=s1 smm.allStu=s2*/ smm.allStu=make(map[string]Student) for k,v:=range s1 smm.allStu[k]=v for k,v:=range s2 smm.allStu[k]=v fmt.Println(smm) //map[1001:zhangsan 19 [90 98 97] 1002:lisi 20 [80 90 95]]
练习补充4:再回顾map
如果不是声明并赋值map[string]string“1001”:stu1这种使用map的方式,
直接使用map[“1001”]=stu1需开辟空间
package main import "fmt" func main() var smm map[string]map[string]string stu1:=map[string]string"sid":"1001","name":"zhangsan","age":"18","yuwen":"99","shuxue":"98","yingyu":"95" smm= map[string]map[string]string"1001":stu1 //smm["1001"]=stu1 //这种报错,没有开辟空间 stu2:=map[string]string"sid":"1002","name":"lisi","age":"19","yuwen":"99","shuxue":"98","yingyu":"95" smm["1002"]=stu2 fmt.Println(smm)
下午10 结构体的指针变量
值拷贝
变量赋值后,修改s2,s.name发生变化吗? 不变,已经是不同的地址空间
package main import "fmt" //声明一个结构体 type Student struct name string age int score map[string]int func main() var s=Studentname:"rain",age: 30,score: map[string]int"语文":100,"数学":90,"英语":80 s2:=s s2.name="yuan" fmt.Println(s.name)
图解
值拷贝,函数传入结构体指针
package main import "fmt" //声明一个结构体 type Student struct name string age int score map[string]int func foo(p *Student) //如果p是一个结构体的指针变量,name执行p.name的时候,编译器会翻译成(*p).name p.name="yuan" fmt.Println(*p) //(*p).name = "yuan" func main() //s是一个结构体对象 var s=Studentname:"rain",age: 30,score: map[string]int"语文":100,"数学":90,"英语":80 //案例1 /*s2:=s s2.name="yuan" fmt.Println(s.name)*/ /* //案例2 foo(s) fmt.Println(s) //不变 rain 30 map[数学:90 英语:80 语文:100]*/ //案例2 var p=&s //是一个Student结构体的指针对象 foo(p) fmt.Println(s)
下午11 模拟构造函数
示例1 通过调用函数,获取结构体对象
package main import "fmt" type Student struct name string age int score map[string]int func NewStudent(name string,age int,score map[string]int) Student return Studentname:name,age:age,score: score func main() //var s = Studentname: "rain", age: 32, score: map[string]int"yuwen": 100, "shuxue": 90, "yingyu": 80 s:=NewStudent("zhangsan",18, map[string]int"语文":100,"数学":90,"英语":89) fmt.Println(s) //zhangsan 18 map[数学:90 英语:89 语文:100]
示例2
package main import "fmt" type Student struct name string // 结构体的成员变量 age int score map[string]int func NewStudent(name string,age int,score map[string]int) *Student return &Studentname:name,age:age,score:score func main() s:=NewStudent("yuan",23, map[string]int"yuwen":100,"shuxue":90,"yingyu":89) fmt.Println(s) fmt.Println(s.name)
下午12 方法接收器
示例1 声明结构体
package main //声明一个结构体 type Player struct name string //结构体的成员变量 healthPoint int magic int func NewPlayer(name string,healthPoint int,magic int) *Player return &Playername:name,healthPoint: healthPoint,magic: magic func main() p:=NewPlay("yuan",100,80) fmt.Println(p.name) fmt.Println(p.healthyPoint) 打印结果 yuan 100
示例2 声明接收器方法
调用是会把yuan赋值给小p
这时候用p.name
package main import "fmt" //声明一个结构体 type Player struct name string //结构体的成员变量 healthPoint int magic int func NewPlayer(name string,healthPoint int,magic int) *Player return &Playername:name,healthPoint: healthPoint,magic: magic func (p Player)attack() fmt.Println(p.name,"发起攻击") func (p Player)attacked() fmt.Println("被攻击") func main() yuan:=NewPlayer("yuan",100,100) rain:=NewPlayer("rain",100,100) yuan.attack() rain.attack() 打印结果 yuan 发起攻击 rain 被攻击
示例3 修改修改被攻击的对象
package main import "fmt" type Player struct name string healthyPoint int magic int func (p Player)attack() fmt.Println(p.name,"发起攻击") func (p Player)attacked() fmt.Println(p.name,"被攻击") p.healthyPoint=p.healthyPoint-30
图示
执行rain.attacked时,rain发生值拷贝,开辟p空间
执行 -30,p发生-30操作
示例4 对外部的值做操作,传指针。
package main import "fmt" //声明一个结构体 type Player struct name string //结构体的成员变量 healthPoint int magic int func NewPlayer(name string,healthPoint int,magic int) *Player return &Playername:name,healthPoint: healthPoint,magic: magic func (p Player)attack() fmt.Println(p.name,"发起攻击") func (p *Player)attacked() fmt.Println("被攻击") p.healthPoint=p.healthPoint-30 func main() yuan:=NewPlayer("yuan",100,100) rain:=NewPlayer("rain",100,100) yuan.attack() rain.attacked() fmt.Println(rain.healthPoint)
下午13 json序列化
是一种数据交换格式
go语言数据类型 |
json支持的类型 |
整型、浮点型 |
整型、浮点型 |
字符串(双引号) |
字符串双引号 |
逻辑值(true或false) |
逻辑值(true或false) |
数组,切片 |
数组(在方括号中) |
map |
对象(再花括号中) |
nil |
null |
示例1 map序列化
package main import ( "encoding/json" "fmt" ) func main() //var map01 = map[int]string1:"111",2:"222",3:"333" var map01=map[string]interface"name":"yuan","isMarried":false,"score":[3]int100,90,89 content,_:=json.Marshal(map01) fmt.Println(string(content)) //"isMarried":false,"name":"yuan","score":[100,90,89]
示例2 struct序列化
package main import ( "encoding/json" "fmt" ) type Student struct name string age int score [3]int func main() s:=Student name:"yuan", age:22, score: [3]int1,2,3, content,_:=json.Marshal(s) fmt.Println(string(content)) //
会打印出来空的,为什么空?
小写,是私有的
需要首字母大写
package main import ( "encoding/json" "fmt" ) type Student struct Name string //结构体成员变量 Age int Score [3]int func main() s:=Student Name:"yuan", Age:22, Score: [3]int1,2,3, content,_:=json.Marshal(s) fmt.Println(string(content)) //"Name":"yuan","Age":22,"Score":[1,2,3]
序列化后,写入文件中
package main import ( "encoding/json" "fmt" "io/ioutil" ) type Student struct Name string //结构体成员变量 Age int Score [3]int func main() s:=Student Name:"yuan", Age:22, Score: [3]int1,2,3, content,_:=json.Marshal(s) fmt.Println(string(content)) //"Name":"yuan","Age":22,"Score":[1,2,3] //写入文件中 //ioutil.WriteFile("data.txt",[]byte(s),0666) //结构体不能直接转 ioutil.WriteFile("data.txt",content,0666)
读文件
读取出来是字符串
package main import ( "fmt" "io/ioutil" ) func main() //读文件 content,_:=ioutil.ReadFile("data.txt") fmt.Println(string(content)) //"name":"yuan","age":18,"score":[1,2,3]
data.txt文件内容
"name":"yuan","age":18,"score":[1,2,3]
反序列化
Unmarshal(字节串,&结构体)
不加&符,发生值拷贝,目的是需要改s2对应的值(即通过指针,修改)
package main import ( "encoding/json" "fmt" "io/ioutil" ) type Student struct Name string `json:"name"` Age int `json:"age"` Score [3]int `json:"score"` func NewStudent(name string, age int, score [3]int) Student return StudentName: name, Age: age, Score: score func main() //读文件 content,_:=ioutil.ReadFile("data.txt") //fmt.Println(string(content)) //"name":"yuan","age":18,"score":[1,2,3] //反序列化 json字符串转换成golang支持的数据类型 var s Student json.Unmarshal(content,&s) fmt.Println(s) //yuan 18 [1 2 3] fmt.Println(s.Name) //yuan fmt.Println(s.Age) // 18
go与js代码交换
JSON.stringify(s)
package main import ( "encoding/json" "fmt" ) func main() //go与js进行json交换 s1:=`"name":"yuan","age":18,"gf":null` var m1=make(map[string]interface) json.Unmarshal([]byte(s1),&m1) fmt.Println(m1) fmt.Println(m1["name"]) 打印结果 map[age:18 gf:<nil> name:yuan] yuan
问题
文件中存的是单引号,是否有影响?
"name":\'yuan\',"age":18,"score":[1,2,3]
是字符串,不是json字符串,
Unmarshal之前必须给个json字符串
package main import ( "encoding/json" "fmt" "io/ioutil" ) type Student struct Name string `json:"name"` Age int `json:"age"` Score [3]int `json:"score"` func main() content,_:=ioutil.ReadFile("data.txt") fmt.Println(string(content)) //"name":\'yuan\',"age":18,"score":[1,2,3] var stu Student json.Unmarshal(content,&stu) fmt.Println(stu) // 0 [0 0 0] 换成单引号,不是json字符串,打印默认值
作业:结构体版 学生管理系统
1、结构体实现
2、持久化
以上是关于Day05 Go语言文件操作,结构体,构造函数,方法接收器,json序列化的主要内容,如果未能解决你的问题,请参考以下文章