Go语言学习(二)
1、递归
递归:自己调用自己
递归适合处理那种问题相同,问题的规模越来越小的场景
递归一定要有一个明确的退出条件
func main(){
fmt.Println(f(5))
}
func f(n uint64) uint64{
if n == 1{
return 1
}
return n * f(n-1)
}
2、定义类型和定义别名
//自定义类型
type myint int
//定义别名
type youint = int
func main(){
var n myint
n = 100
fmt.Println(n)
fmt.Printf("%T\n",n)
var m youint
m = 500
fmt.Println(m)
fmt.Printf("%T\n",m)
}
效果如下所示:
PS D:\go> go run f.go
100
main.myint
500
int
3、匿名结构体
要理解匿名结构体的应用方向
func main() {
//匿名结构体主要是用于临时场景
var s struct {
name string
age int
}
s.name="超峰"
s.age = 20
fmt.Printf("%v\n",s)
}
4、创建指针类型的结构体
type person struct{
name,sex string
}
// go语言里面函数永远传的是拷贝
func f(p person){
p.sex = "女" // 修改额是副本的sex,而不是原来的sex
}
func f1(p *person){
//(*p).sex = "女" //根据内存地址找到哪个变量,修改的就是原来的变量
p.sex = "女" //跟上面的都是一个效果,程序会自动判断p是什么类型,自动转化成(*p),这是语法糖
}
func main() {
var p person
p.name ="超峰"
p.sex = "男"
f(p)
fmt.Println(p.sex)
f1(&p)
fmt.Println(p.sex)
}
效果如下:
PS D:\go> .\f.exe
男
女
指针类型分析
func main(){
var a = 10
b := &a
fmt.Printf("%T\n",b) //打印b的类型
fmt.Printf("%p\n",b) //以十六进制显示 ,b指针保存的是a的内存地址
fmt.Printf("%v\n",b) //以默认格式显示
fmt.Printf("%p\n",&b) //打印b指针对应的内存地址,因为b指针也有自己的内存地址
}
效果如下所示:
PS D:\go> .\g.exe
*int
0xc0000ac068
0xc0000ac068
0xc0000d8018
结构体类型的指针
type person struct{
name,sex string
}
func main() {
//结构体指针1
var p2 = new(person) //p2是一个指针类型的结构体
p2.sex = "男"
fmt.Printf("%s\n",(*p2).sex)
fmt.Printf("%s\n",p2.sex)
fmt.Printf("%T\n",p2) //得到指针类型
fmt.Printf("%p\n",p2) //p2保存的就是一个内存地址
fmt.Printf("%p\n",&p2) //求p2的内存地址
//结构体指针2
// 2.1、key-value方式初始化
var p3 = person{
name: "特朗普",
sex: "男",
}
fmt.Printf("%#v\n",p3)
// 2.2、值传递的方式初始化
var p4 = person{
"小王子",
"男",
}
fmt.Printf("%#v\n",p4)
}
效果如下所示:
PS D:\go> .\f.exe
男
男
*main.person
0xc00005c3c0
0xc000006028
main.person{name:"特朗普", sex:"男"}
main.person{name:"小王子", sex:"男"}
5、构造函数
type person struct{
name string
age int
}
type dog struct{
name string
}
//构造函数:约定俗称用new开头
//返回的是结构体还是结构体指针
//当结构体比较大的上海尽量使用结构体指针,减少程序的内存开销
func newPerson(name string, age int) *person{
return &person {
name: name,
age : age,
}
}
func newdog(name string) dog{
return dog{
name: name,
}
}
func main(){
res1 := newPerson("超峰",20)
fmt.Println(*res1)
res2 := newdog("Mike")
fmt.Println(res2)
}
效果如下所示:
PS D:\go> .\f.exe
{超峰 20}
{Mike}
构造函数和方法区别
type dog struct {
name string
}
// 构造函数
func newDog(name string) dog{
return dog{
name: name,
}
}
//方法是作用于特定类型的函数
// 接受者表示的是调用该方法的具体类型变量,多用类型名首字母小写表示,不建议使用this,self来表示
func (d dog) wang(){
fmt.Printf("%s: 汪汪汪",d.name)
}
func main(){
d1 := newDog("Mike")
d1.wang()
}
效果如下所示L:
PS D:\go> .\g.exe
Mike: 汪汪汪
5、值传递和指针传递的区别
先说说什么时候应该使用指针类型的接收者
a、需要修改接收者中的值
b、接收者是拷贝代码比较大的对象
c、保证一致性,如果使用了指针传递的方式,尽量都使用这种方法
举个例子:
package main
import "fmt"
type dog struct{
name string
}
type person struct{
name string
age int
}
func newPerson(name string,age int) person{
return person{
name: name,
age: age,
}
}
func newDog(name string) dog{
return dog{
name: name,
}
}
// 使用值接收者:传拷贝进去
func (d dog)wang(){
fmt.Printf("%s:汪汪汪\n",d.name)
}
// 指针接收者:传内存地址进去
func (p *person)guonian(){
p.age++
}
func main(){
d1 := newDog("Mike")
d1.wang()
p1 := newPerson("John",20)
p1.guonian() //相当于把p1的地址传到guonian()这个函数里面,这样子写比较好理解(&p1).guonian(),但实际上不这样用
fmt.Println(p1.name,p1.age)
}
效果如下所示:
PS D:\go> .\f.exe
Mike:汪汪汪
John 21
顺便补充一个自定义类型的知识点
// 不能给别的包里面的类型增加自定义方法,只能给自己的包里的类型添加方法
type myint int
func (m myint) f1(){
fmt.Println("hello")
}
func main(){
m := myint(100) // 与int(100)类似的效果。
m.hello
}
函数版学生管理系统
package main
import (
"fmt"
"os"
)
type student struct{
id int
name string
}
var (
allstudent map[int]*student //变量声明
)
func showAllstudent() {
for k,v := range allstudent{ // map里面存储的是 map[1:{1,"Mike"},2:{2:"John"}]
fmt.Printf("学生id是: %d, 姓名是: %s\n",k,v.name)
}
}
func newstu(id int,name string) *student{
return &student{
name: name,
id: id,
}
}
func addstudent() {
var (
id int
name string
)
fmt.Print("请输入增加的学号id: ")
fmt.Scanln(&id)
fmt.Print("请输入增加的学生姓名: ")
fmt.Scanln(&name)
stu := newstu(id,name)
allstudent[id] = stu
fmt.Println("增加成功")
}
func deletestudent(){
var (
id int
)
fmt.Print("请输入你要删除的学号id: ")
fmt.Scanln(&id)
delete(allstudent,id)
fmt.Println("删除成功")
}
func main(){
allstudent = make(map[int]*student,48)
for{
fmt.Printf("欢迎来到学生管理系统")
fmt.Println(`
1、查看所有学生
2、增加学生
3、删除学生
4、退出
`)
fmt.Printf("请输入你要操作的序号: ")
var choice int
fmt.Scanln(&choice)
// fmt.Printf("你当前输入的序号是: %d",choice)
switch choice {
case 1:
showAllstudent()
case 2:
addstudent()
case 3:
deletestudent()
case 4:
os.Exit(1)
default:
fmt.Println("滚")
}
}
}
6、匿名结构体
package main
import (
"fmt"
)
type address struct{
provice string
city string
}
type person struct{
name string
age int
addr address
}
type company struct{
name string
addr address
}
func main(){
p1 := person {
name: "Mike",
age: 20,
addr: address{
provice: "北京",
city: "朝阳",
},
}
c1 := company {
name: "牛卡福",
addr: address {
provice: "北京",
city: "朝阳",
},
}
fmt.Println(p1)
fmt.Println(p1.name,p1.addr.city,p1.addr.provice)
fmt.Println("----------------------------")
fmt.Println(c1)
fmt.Println(c1.name,c1.addr.provice)
}
7、结构体之继承
如下例子所示:狗继承了动物会走的特性,狗调用自己会叫的特性,同时继承了动物会走的特性
type animal struct{
name string
}
func (a animal) move(){
fmt.Printf("%s会动\n",a.name)
}
type dog struct {
feet uint8
animal
}
func (d dog)wang(){
fmt.Printf("%s会叫: 汪汪汪\n",d.name)
}
func main(){
f1 := animal{name: "动物"}
f1.move()
f2 := dog{
feet: 4,
animal: animal{
name: "狗",
},
}
f2.wang()
f2.move()
}
效果如下所示:
PS D:\go> .\i.exe
动物会动
狗会叫: 汪汪汪
狗会动
8、结构体与json
1、序列化:把Go语言中的结构体变量转化为json格式的字符串
2、反序列化:把json格式的字符串转化为Go语言中能够识别的结构体变量
例子:
package main
import (
"fmt"
"encoding/json"
)
type person struct {
name string
age int
}
func main(){
p1 := person{
name: "Mike",
age: 18,
}
b,err := json.Marshal(p1)
if err != nil{
fmt.Printf("Marshal failed, err: %v",err)
return
}
// 将返回的字节强转为string字符串
fmt.Printf("%#v\n",string(b))
}
效果如下所示:
PS D:\go> .\i.exe
"{}"
我们使用了%#v
,这是以go语法来进行了打印。看的出来是转化成了字符串。
但是我们发现里面的json字串是空的,这是因为结构体的字段名需要大写,我们是把main包里面的结构体字段传入到json包里面,字段小写的话是穿不了的,需要改成首字母大写的形式,如下所示:
# 这里只修改部分代码即可
type person struct {
Name string
Age int
}
效果如下所示
PS D:\go> .\i.exe
"{\"Name\":\"Mike\",\"Age\":18}"
看的出来现在已经转化了,比较成功。
可以把%#v
改成%v
,这样子的话打印出来的结果里面就不会有双引号了,是默认打印的结果,如下所示
PS D:\go> .\i.exe
{"Name":"Mike","Age":18}
上面这种用法会用在前后端的交互中,有时候前端要接收全部小写的单词,那么我们上面是返回的结果是首字母大写的字段,所以这是不行的,可以如下所示进行修改
type person struct {
Name string `json:"name"`
Age int `json:"age"`
}
# 后面都不需要修改,再看下效果
效果
PS D:\go> .\i.exe
{"name":"Mike","age":18}
这返回的结果中,就是小写了,符合前端需要的标准
接下来演示反序列化的代码
package main
import (
"fmt"
"encoding/json"
)
type person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main(){
p1 := person{
Name: "Mike",
Age: 18,
}
b,err := json.Marshal(p1)
if err != nil{
fmt.Printf("Marshal failed, err: %v",err)
return
}
// 将返回的字节强转为string字符串
fmt.Printf("%v\n",string(b))
//反序列化
str := `{"name":"John","age":20}`
var p2 person
json.Unmarshal([]byte(str), &p2) //注意这里需要传入指针,传指针式为了能在json.Unmarshal内部修改p2的值
fmt.Printf("%v\n", p2)
}
效果如下所示:
PS D:\go> .\i.exe
{"name":"Mike","age":18}
{John 20}
知识点总结:
1、架构体内部的字段首字母要大写,不大写别人是访问不道德
2、反序列化需要传递指针
回顾和总结
package main
import (
"fmt"
)
type tmp struct{
id int
name string
}
type person struct{
name string
age int
}
func newPerson(name string,age int) person {
return person{
name: name,
age: age,
}
}
//只有接收者类型的变量才可以调用
func (p person)hoppy(str string){
fmt.Printf("%s的爱好是: %s\n",p.name,str)
}
//下面这种是修改的结构体的副本
func (p person)guonian2(){
p.age++
}
//指针接收者,强烈建议使用
//当需要修改结构体的变量的值的时候,就可以使用指针接收者
func (p *person)guonian(){
p.age++
}
func main(){
var b = tmp{100,"John"}
fmt.Println(b)
var a = struct{
id int
name string
}{10,"Mike"}
fmt.Println(a)
// 结构体的实例化的三种方法
var p1 person
p1.name = "John"
p1.age = 20
//匿名返回值
p2 := person{"Simon",30}
//构造函数返回值,返回值的是对应结构体类型
p3 := newPerson("Mike",40)
fmt.Println(p1,p2,p3)
p1.hoppy("羽毛球")
p2.hoppy("篮球")
p3.hoppy("网球")
p1.guonian()
p2.guonian()
p3.guonian()
fmt.Println(p1,p2,p3)
var f = "Mike"
fmt.Printf("%v",[]byte(f))
}
效果如下所示:
PS D:\go> go build .\k.go
PS D:\go> .\k.exe
{100 John}
{10 Mike}
{John 20} {Simon 30} {Mike 40}
John的爱好是: 羽毛球
Simon的爱好是: 篮球
Mike的爱好是: 网球
{John 21} {Simon 31} {Mike 41}
[77 105 107 101]
补充两个知识点
1、类型别名只是在代码编写过程周有效,编译完成转换就不存在
2、内置的byte和rune都是类型别名
3、字符串类型可以和byte类型的切片进行转换
9、接口
接口的定义
type 接口名 interface {
方法名1(参数1, 参数2....)(返回值1,返回值2...)
方法名2(参数1, 参数2....)(返回值1,返回值2...)
......
}
接口的实现
一个变量如果实现了接口中规定的所有的方法,那么这个变量就实现了这个接口,可以称为这个接口类型的变量
type animal interface {
move()
eat(string)
}
type cat struct{
name string
feet int8
}
func (c cat)move(){
fmt.Println("猫会动")
}
func (c cat)eat(s string){
fmt.Printf("猫吃%s\n",s)
}
type chicken struct{
feet int8
}
func (c chicken) move(){
fmt.Println("鸡会动")
}
func (c chicken)eat(s string){
fmt.Printf("鸡吃%s\n",s)
}
func main(){
var a1 animal //定义一个接口类型的变量,默认是nil
bc := cat{ //定义一个cat类型的变量bc
name: "蓝猫",
feet: 4,
}
a1 = bc
fmt.Println(a1)
a1.move()
a1.eat("猫粮")
//查看类型
fmt.Printf("%T\n",a1)
}
效果如下:
PS D:\go> .\l.exe
{蓝猫 4}
猫会动
猫吃猫粮
main.cat
10、使用值接收者实现接口与使用指针接收者实现接口的的方法
使用值接收者实现接口,结构体类型和结构体指针类型的变量都能存
使用指针接收者实现接口只能存结构体指针类型的变量
type animal interface {
move()
eat(string)
}
type cat struct{
name string
feet int8
}
//使用指针接收者实现了接口的所有方法
func (c *cat)move(){
fmt.Println("猫会动")
}
func (c *cat)eat(s string){
fmt.Printf("猫吃%s\n",s)
}
type chicken struct{
feet int8
}
func (c chicken) move(){
fmt.Println("鸡会动")
}
func (c chicken)eat(s string){
fmt.Printf("鸡吃%s\n",s)
}
func main(){
var a1 animal //定义一个接口类型的变量,默认是nil
bc := cat{ //定义一个cat类型的变量bc
name: "蓝猫",
feet: 4,
}
a1 = &bc //实现animal这个接口的cat的指针类型
fmt.Println(a1)
a1.move()
a1.eat("猫粮")
bc1 := &cat{name:"白猫",feet:4}
a1 = bc1
a1.move()
a1.eat("小鱼干")
}
效果如下所示L:
PS D:\go> .\l.exe
&{蓝猫 4}
猫会动
猫吃猫粮
猫会动
猫吃小鱼干
接口嵌套
type Animal1 interface {
SetName(string)
}
type Animal2 interface {
GetName() string
}
type Animal interface{
Animal1
Animal2
}
type Dog struct{
name string
}
func (d *Dog)SetName(name string){
d.name = name
}
func (d *Dog)GetName()string{
return d.name
}
func main(){
var dog = &Dog{name:"Mike",}
var d Animal
d = dog
d.SetName("小狗")
fmt.Println(d.GetName())
}
效果如下所示:
PS D:\go> .\l.exe
小狗
匿名函数的补充
匿名函数就是没有名字的函数
func main(){
f := func(i int){
fmt.Printf("Hello: %d\n",i)
}
f(2)
f1 := func(i int){
fmt.Printf("结果是: %d\n",i*i*i)
}
f1(3)
}
效果如下所示:
PS D:\go> .\l.exe
Hello: 2
结果是: 27
11、空接口
当不知道保存什么类型的值的时候,可以定义一个空接口类型
func main(){
var a map[string]interface{}
a = make(map[string]interface{},48)
a["name"] = "Mike"
a["年龄"] = 20
a["hobby"] = [...]string{"唱","跳","rap"}
fmt.Println(a)
}
效果如下所示:
PS D:\go> .\l.exe
map[hobby:[唱 跳 rap] name:Mike 年龄:20]
类型断言:
主要是使用变量.(类型)
方式来判定
func main(){
var a interface{} //声明一个空接口类型
a = 100 //此时a是int64类型的
v1,ok := a.(int8)
if ok{
fmt.Println("猜对了,是int8类型",v1)
}else {
fmt.Println("猜错了")
}
//第二种方式,使用类型断言,用type判断这是什么类型
switch v2 := a.(type) {
case int8:
fmt.Println("int8",v2)
case int16:
fmt.Println("int16",v2)
case int32:
fmt.Println("int32",v2)
case int64:
fmt.Println("int64",v2)
case string:
fmt.Println("string",v2)
case int:
fmt.Println("int",v2)
}
}
效果如下所示:
PS D:\go> .\n.exe
猜错了
int 100
12、包package
如果想在一个包中引用另外一个包里面的标识符(如变量,常量,类型,函数等)时,该标识符必须是对外可见的(public)。在go语言中只需要将标识符的首字母大写就可以让标识符对外可见了,这也是为什么我们使用fmt.Println()的时候,P这个字母大写的原因。
使用方式如下:
a、先定义GOPATH变量,这是Go的工作空间
可以使用go env命令查看。
我这里是windows机器,假设GOPATH=D:\go
b、定义src目录
此时需要在D:\go
下面有一个src目录,手动创建。
c、接下来
在D:\go\src
目录下创建clac目录,并写入文件
package calc
func Sum(x int,y int) int{ // Sum首字母大写,否则其他包找不到这个函数
return x + y
}
d、接着是main包
必须有一个main包
package main
import (
"calc" //这里找的就是D:\go\src\calc目录
"fmt"
)
func main(){
s := calc.Sum(10,20)
fmt.Println(s)
}
包的自定义别名
import 别名 项目
13、init()初始化函数
go语言程序执行时导入包语句会自动触发内部的init()函数的调用,需要注意,init()函数没有参数,也没返回值,init()函数是自动被调用执行,不能在代码中主动调用它,比如
package calc
import (
"fmt"
)
func init(){
fmt.Println("这是sum函数")
}
func Sum(x int,y int) int{
return x + y
}
main包
package main
import (
"calc"
"fmt"
)
func init(){
fmt.Println("这是main函数")
}
func main(){
s := calc.Sum(10,20)
fmt.Println(s)
}
执行效果如下所示:
PS D:\go> .\m.exe
这是sum函数
这是main函数
30
如下流程图所示:
还有一个初始化函数的执行顺序如下图所示:
14、文件操作
使用bufio模块
package main
import (
"fmt"
"os"
"io"
"bufio"
)
func main(){
fileObj,err := os.Open("./e.go")
if err != nil{
fmt.Println("Open file failed!!!")
return
}
//记得关闭文件
defer fileObj.Close()
//创建一个用来从文件中读内容的对象
reader := bufio.NewReader(fileObj)
// 循环打印每一行内容
for{
line,err := reader.ReadString('\n')
// 读取到文件末尾就退出
if err == io.EOF {
return
}
if err != nil{
fmt.Printf("read line failed")
return
}
fmt.Print(line) //这里要使用print, 不要换行
}
}
文件内容比较多,这里不再展示了
使用ioutil工具读取文件,这个工具大概每次读取512个字节
package main
import (
"fmt"
"io/ioutil"
)
func main(){
ret,err := ioutil.ReadFile("./e.go")
if err != nil{
fmt.Println("文件读取失败!!!")
return
}
//ret是字节,这里需要string转化一下
fmt.Print(string(ret))
}
15、写文件操作得几种方式
package main
import (
"fmt"
"bufio"
"os"
"io/ioutil"
)
func writefileDemo1(){
// 读写文件,如果没有就创建,追加文件内容
fileObj,err := os.OpenFile("./test.txt",os.O_WRONLY|os.O_CREATE|os.O_APPEND,0644)
if err != nil{
fmt.Println("Open file failed,err:%v",err)
return
}
fileObj.Write([]byte("Hello world\n"))
fileObj.WriteString("You are welcome\n")
fileObj.Close()
}
func writefileDemo2(){
fileObj,err := os.OpenFile("./test.txt",os.O_WRONLY|os.O_CREATE|os.O_APPEND,0644)
if err != nil{
fmt.Println("Open file failed,err:%v",err)
return
}
defer fileObj.Close()
wr := bufio.NewWriter(fileObj)
wr.WriteString("hello沙河")
wr.Flush() //将缓存中得内容写入文件
}
func writefileDemo3(){
//下面这种不能追加,需要额外的机制来解决
str := "hello world"
err := ioutil.WriteFile("./test.txt", []byte(str),0666)
if err != nil{
fmt.Println("Write file failed ,err:",err)
return
}
}
func main(){
//writefileDemo1()
//writefileDemo2()
writefileDemo3()
}
补充知识点:
使用fmt.Scanln(str)
从终端读取得缺点是不能遇到空格,否则只能识别第一个空格前面的字符串,如下所示:
func main(){
var str string
fmt.Print("请输入内容: ")
fmt.Scanln(&str)
fmt.Println("输入的内容是: ",str)
}
效果如下所示:
PS D:\go> .\n.exe
请输入内容: chen chao feng
输入的内容是: chen
用下面这种方法进行更改
func main(){
var s string
reader := bufio.NewReader(os.Stdin)
fmt.Println("请输入内容: ")
s,_ = reader.ReadString('\n')
fmt.Printf("你输入的内容是: %s\n",s)
}
效果如下所示:
PS D:\go> .\n.exe
请输入内容:
Hello world
你输入的内容是: Hello world
go类型转换,字节和整型的转换
//整形转换成字节
func IntToBytes(n int) []byte {
x := int32(n)
bytesBuffer := bytes.NewBuffer([]byte{})
binary.Write(bytesBuffer, binary.BigEndian, x)
return bytesBuffer.Bytes()
}
//字节转换成整形
func BytesToInt(b []byte) int {
bytesBuffer := bytes.NewBuffer(b)
var x int32
binary.Read(bytesBuffer, binary.BigEndian, &x)
return int(x)
}
16、时间的各种使用
func main(){
now := time.Now()
fmt.Println(now)
fmt.Println(now.Year())
fmt.Println(now.Month())
fmt.Println(now.Day())
fmt.Println(now.Hour())
fmt.Println(now.Minute())
fmt.Println(now.Second())
fmt.Println("++++++++++++++++++++++++++++++++")
//时间戳,微秒
fmt.Println(now.Unix())
//纳秒
fmt.Println(now.UnixNano())
fmt.Println("++++++++++++++++++++++++++++++++")
//time.Unix()
ret := time.Unix(1564803667, 0)
fmt.Println(ret)
fmt.Println(ret.Year())
fmt.Println(ret.Day())
fmt.Println("++++++++++++++++++++++++++++++++")
// 明天的这个时候
fmt.Println(now.Add(time.Hour * 24))
fmt.Println("################################")
//格式化时间输出。go是从诞生日这种格式来用的
fmt.Println(now.Format("2006-01-02"))
// 得到年月日时分秒的格式
fmt.Println(now.Format("2006/01/02 15:04:05"))
//将对应的格式解析成字符串类型的时间
timeObj, err := time.Parse("2006-01-02", "2021-08-20")
if err != nil {
fmt.Printf("parse time failesd, err: %v\n",err)
return
}
fmt.Println(timeObj)
fmt.Println(timeObj.Unix()) //将对应的时间格式转化成其他的格式,非常好用
}
效果如下所示:
PS D:\go> .\n.exe
2021-08-20 19:32:26.007833 +0800 CST m=+0.003088801
2021
August
20
19
32
26
++++++++++++++++++++++++++++++++
1629459146
1629459146007833000
++++++++++++++++++++++++++++++++
2019-08-03 11:41:07 +0800 CST
2019
3
++++++++++++++++++++++++++++++++
2021-08-21 19:32:26.007833 +0800 CST m=+86400.003088801
################################
2021-08-20
2021/08/20 19:32:26
2021-08-20 00:00:00 +0000 UTC
1629417600
17、获取文件的大小和文件名称
package main
import (
"fmt"
"os"
)
func main(){
//打开当前文件,os.Open()多用来读取文件
fileObj, err := os.Open("p.go")
if err != nil{
fmt.Printf("open file failed ,err: %v\n", err)
return
}
// 1、文件对象的类型
fmt.Printf("%T\n", fileObj)
// 2、获取文件对象的详细信息
fileInfo, err := fileObj.Stat()
if err != nil{
fmt.Printf("get file info failed, err: %v\n", err)
return
}
fmt.Printf("文件大小是: %d B\n",fileInfo.Size())
fmt.Printf("文件名是: %s\n",fileInfo.Name())
}
效果如下所示:
PS D:\go> .\p.exe
*os.File
文件大小是: 560 B
文件名是: p.go