P75 今日内容
P76内容回顾
一个文件夹就是一个包,包里都是.go文件。
包导入的时候可以起别名,可以匿名导入包。
包里面的标识符,需要首字母大写,才被外部可见,可以调用
包导入的路径,是从$GOpath的src后面的路径开始写起
init是包导入的时候会自动执行,一个包里只有一个init,init没有参数也没有返回值,也不能手动调用,一般用于初始化操作,一般初始化配置文件,数据库链接等。
**接口是一种特殊类型,是你要实现的方法的清单。
实现了接口的方法就实现了这个接口,就可以作为这个接口类型的变量。
**
空接口,代表了一个接口里没有方法清单,一个方法都没有,那么所有类型都实现了一个空接口,可以把任何类型存到空接口里,interFace{}表示一个空接口。
一般作为函数参数,fmt.println就是 一个空接口 ,或者map里想存任意类型的值可以用空接口
接口底层其实分两部分,一个 动态类型,一个动态值,类型断言就是判断类型是什么类型的。前提是判断的类型是个接口类型的变量。
T大t一般在go语言里代表type。
interface{}就代表一个空接口。
有多个判断,可以使用switch,现在只能猜,用了反射才能知道确切的类型
文件操作,打开关闭,读写,程序也是通过系统调用去操作文件,如果不释放io,就会让操作系统资源耗尽。正常大公司服务器都是3年报废,或者退居2线,重要的生产三天两头出问题,谁也受不了了。
打开一个问获得文件对象,如果错误返回错误码,判断错误的时候不为空,就一定有错误
要用下面的方式写,go语言可以有多个返回值,如果f1上面的错误了,那么fileobj就是nil,nil就没有close方法,就返回给你一个空指针引用
读文件三种方法,bufio会有一个中间状态,缓冲区,读到缓冲区,达到缓冲区的阈值就返回到代码。写的时候缓冲区调用flush才能写,但是中间断电,数据就丢了
P77 在文件中间插入内容
所有文件相关的操作都可以在标准库里查找。
seek(1,0)1个字节,从0开始,读一个切片
windows这里有/r/n
所以 需要移动三个字符,光标移动到b,3代表移动三个位置,0代表从什么地方开始
像这样不换行就会读到b了
往文件里写,需要额外声明一个切片,拿到上面读到的切片。
切片初始化只能用make,不能用new
这样就相当于往文件写了C
相当于把原来的b覆盖了
插入数据的话,只能先新建一个文件,读出来,再出入到新的文件里,所有语言都不可能读一个文件,从中间插入数据。
读文件写入临时文件,再写入要插入的内容,
紧接着把源文件后续的内容写入临时文件
P78 time包
time.Time代表时间类型
时间戳是从1970年1月1日8点,到当前的总毫秒数
获取时间戳,nano纳秒
时间戳转换成时间格式
时间间隔
时间操作+
明天的时间
时间就一秒钟 给一个值
go的时间 格式化模板是它的诞生时间 ,2006年1月2日15点04分,也就是 2006 1 2 3 4
转换成字符串类型的时间,格式化
![zhuanzhuan![](https://img-blog.csdnimg.cn/76be4b8dc3024fccb2637f1f5c027f30.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBANDhONkU=,size_20,color_FFFFFF,t_70,g_se,x_16)
想要格式是2006/01/02 11:11:11
加上上午下午
加上毫秒
字符串类型转换成时间戳
P79 time包补充
但凡调用其他包里的东西,首字母都需要大写,小写调用不到
相差3600多个小时
可以试试UTC时间
减的是一个时间对象,返回值是个 时间间隔,
指定时区
sleep,函数名(参数)没有返回值,参数是一个时间间隔的类型
**duration是一个基于int64的类型,定义了两个常量,只能在time包里使用,需要判断传进来的参数是否符合要求,最大值最小值
**
这样赋值了传进去就不匹配了,类型不能转换
需要显示转换类型
P80 日志库需求分析
先看一下时区的问题,默认是获取的本地的时间
明天的当前时间,按照东八区的时区和格式去解析一个字符串的时间。time.loadlocation根据字符串加载时区
按照指定时区解析时间parseinlocation,这样就获得了正确的东八区相减
P81 日志库简单实现
go语言的日志可以相对比较简单
打印的时候会在前面加默认的时间戳
打开一个文件,把日志输出到文件
日志库的需求:
1支持往不同的地方输出日志
2日志级别:debug,info,warning,error,fatal严重错误
3.日志支持开关
4.日志要有基本格式,时间,行号,文件名,级别,日志信息
5.日志文件要切割
需要自己写的程序调用自己写的日志库
日志库开发,fprint是往文件里去写
大概的架子
写一个for循环调用
这些只是最简单的实现,离需求还差点
添加时间格式,并换行
再把日志级别加上去
日志开关,比如开发的时候什么级别都可以输出 ,上线之后只有INFO级别输出
基于内置的int16类型造了一个自己的类型level。iota第一个是0,每一行加1
需要实现一个方法,获得字符串的级别,解析成内置类型的级别,parseloglevel
如果传的类型什么都不是,可以返回一个unknown,错误
error.mew造一个新的错误
errors
用户传进来字符串的时候,需要解析。
构造一个logger对象,里面有一个日志级别
如果上线就判断只输出info级别的日志
每个函数都进行调用
如果是debug级别,相当于其他任何级别都可以输出,所以是>=
P82 runtime.caller
可以添加一个行号,runtime是一个进行时,go自带gc,caller,传一个int参数,就可以拿到一个当前程序的隔了对应多少层,如果ok是true就可以拿到,如果是false,就拿不到。pc可以拿到函数的一些信息,file是当前执行的哪个文件,line是第几行
main函数直接调用,所以直接传入0.
main函数是程序的入口函数,需要传其他数了,默认就是0
调用文件的全部路径。行数11
假如把这段代码封装到函数里,在main函数里调用,就隔了一层,打印的行号一定是执行runtime.caller的行号
想要获取21行,这一行调用,就等于再往上找一层,就是21行
那么再往上调用,再找一层就是25行了
拿到函数名,pc
拿到最终的f函数,写个0即可
这样就拿到f1
拿到文件名
在外部进行调用
在记录日志的地方进行调用
但是一个个 添加太麻烦,可以写到一个log函数里
再写一个把日志级别进行转换的函数getLogString
现在调用就有行号了
函数名 可以做一下分割
P83 记录日志时支持格式化输出
导入自己开发的包并进行调用
谁调用就记录这个是谁调用的信息
可以加一个格式参数,后面可以传任何类型的参数等于是传的信息,和a是一个格式化的关系
可以往里面传字符串的格式化,可以传任意格式的进去
P84 实现往文件里记录日志
改个名字,区分一下
把日志存到文件里
newfiledlogger构造函数
把级别复制过来
可以加个参数,输出的地方不同
都替换一下
这样判断就可以移到一个函数里了,直接打印就行
这样就更简单了
文件版的enable
但是肯定要一开始打开文件就初始化好
按照文件路径和文件名将文件打开,写日志都是追加,没有就创建,这样返回一个文件对象。
如果打开文件失败了就记录一下。
日志文件打不开就没必要往下走了,还有一个专门输出error日志的文件
关闭文件
往文件里去记录
如果要记录的日志等级大于等于error,还要在error日志文件中再记录一份
传入参数
现在就有日志了
P85 日志文件切割
可以按文件大小切割,和按日期切割
判断传入的文件是否是err,返回os.file结构体的指针
原始包里提供的时一个接口类型,里面有一些方法,返回值
获取文件对象的详细信息
上面的代码移到之前的代码里,获取传进来的文件大小,当前文件大小大于等于日志文件最大值就返回true
在下面调用checksize。
进行日志切割
1.关闭当前日志文件
2.备份一下 rename
3.打开一个新的日志文件
4.将打开的新日志文件对象赋值给f.fileobj
重命名后,打开一个新的日志文件,往里面继续写日志
下面记录错误日志的就也需要,把切割的代码部分抽出去变成一个函数
这里其实可以打印文件名
还需要判断切的时info日志还是err日志
返回增加一个nil,split还需要接收对象
返回的错误文件对象
P86 日志库补充
把原来的日志文件先删除
报错是因为文件关闭了,就代表拿不到文件信息,
关闭文件放到下面
备份
initfile的作用是创建文件实例的时候,需要做一个初始化
写的这些方法的作用
文件日志结构体
console和记录文件的都需要实现日志界别,也就是可以定义interface接口
定义接口后,可以定义一个日志实例,在main包里的任何地方去使用,或者其他包里去使用。
定义一个构造函数
两个都可以赋值
也可以包装起来,传不同参数时候,可以进行switch
上面就是申明一个全局的接口变量
P87 反射及附加题需求
之前写的类型,在go语言执行的时候就已经知道了是什么类型,反射只有执行到指定的行才知道是什么类型
反射可以动态获取
这样是可以传进去的,但是执行会报错
任何接口值都是由一个具体类型和具体类型的值两部分组成
也就是动态类型和动态类型的值
这样就能拿到类型
类型分为了type和kind,kind对应底层造的类型
这样就打印出来main.cat
类型,种类kind结构体
可以基于内置的类型造一个自己的类型
valueof返回一个reflect.value的类型,其中包含原始值信息,和原始值之间可以互相转换。
这里取到接口传过来的变量,如果种类满足int64,就转换成int64;如果是float32就转成float32
复制到这里进行测试
下面拿到的是值的类型
通过反射设置变量的值,上面是读变量的值,go语言的函数传值永远是拷贝,但凡修改值的时候,一定要return。
想要修改这个100,但是提示需要传一个指针
传一个指针,但是没有修改成功,函数还需要修改
在函数里这样使用也就获得了地址
这样就直接修改了
isnil判断特定的值是否未null,特有的值必须是通道,函数,接口,指针,映射,切片
结构体字段在反射里也有类型structfield
学生结构体有名字和分数
可以返回结构体字段数目
增加多个值,测试下面获取
现在就获取到了
反射有好处,有坏处。
好处:让代码更加灵活。
坏处:牺牲了一些代码的性能
创建一个ini文件
造一个结构体
把文件里的值拿出来,这是需要实现的
可以加上ini解析