自定义包

package _0calc

import (
"fmt"
) /*
[Go语言的包]
在工程化的Go语言开发项目中,Go语言的源码复用是建立在包(package)基础之上的
本文介绍了Go语言中如何定义包、如何导出包的内容及如何导入其他包 [包介绍]
包(package)是多个Go源码的集合,是一种高级的代码复用方案,Go语言为我们提供了很多内置包 如fmt、os、io等 [定义包]
我们还可以根据自己的需要创建自己的包,一个包可以简单理解为一个存放 .go 文件的文件夹
该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包
package 包名
注意事项:
一个文件夹下面直接包含的文件只能归属一个package,同样一个package的文件不能在多个文件夹下
包名可以不和文件夹的名字一样,包名不能包含 - 符号
包名为main的包为应用程序的入口包,这种包编译后会得到一个可执行文件,而编译不包含main包的源代码则不会得到可执行文件 [可见性]
如果想在一个包中引用另外一个包里的标识符(如变量、常量、类型、函数等)时,该标识符必须是对外可见的(public)
在Go语言中只需要将标识符的首字母大写就可以让标识符对外可见了 [包的导入]
要在代码中引用其他包的内容,需要使用import关键字导入使用的包。具体语法如下:
import "包的路径"
注意事项:
import导入语句通常放在文件开头包声明语句的下面
导入的包名需要使用双引号包裹起来
包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔
Go语言中禁止循环导入包
单行导入
import "包1"
import "包2"
多行导入
import (
"包1"
"包2"
) [自定义包名]
在导入包名的时候,我们还可以为导入的包设置别名。通常用于导入的包名太长或者导入的包名冲突的情况。具体语法格式如下:
import 别名 "包的路径"
import "fmt"
import m "github.com/Q1mi/studygo/pkg_test"
func main() {
fmt.Println(m.Add(100, 200))
fmt.Println(m.Mode)
} [匿名导入包]
如果只希望导入包,而不使用包内部的数据时,可以使用匿名导入包。具体的格式如下:
import _ "包的路径"
匿名导入的包与其他方式导入的包一样都会被编译到可执行文件中。 [init()初始化函数]
在Go语言程序执行时导入包语句会自动触发包内部init()函数的调用
需要注意的是:
init()函数没有参数也没有返回值。
init()函数在程序运行时自动被调用执行,不能在代码中主动调用它 init()函数执行顺序
Go语言包会从main包开始检查其导入的所有包,每个包中又可能导入了其他的包。
Go编译器由此构建出一个树状的包引用关系,再根据引用顺序决定编译顺序,依次编译这些包的代码
在运行时,被最后导入的包会最先初始化并调用其init()函数,如图导入包init执行顺序.png
*/ // init函数
func init() {
fmt.Println("被导入的时候,我是自动执行的...")
} // 包中的标识符(变量名\函数名\结构体\接口等)如果首字母是小写的,表示私有的(只能在当前这个包使用)
// 首字母大写的标识符可以被外部的包调用
func Add(x, y int) int {
return x + y
}

自定义包的案例

目录结构

├── 10calc
│   └── calc.go
├── 11import_demo
│   ├── main.go

calc.go文件

package _0calc

import (
"fmt"
) // init函数
func init() {
fmt.Println("被导入的时候,我是自动执行的...")
} func Add(x, y int) int {
return x + y
}

main.go文件

package main

import (
zhoulin "gostudy/day05/10calc"
"fmt"
) var x = 100 const pi = 3.14 func init() {
fmt.Println("自动执行!")
fmt.Println(x, pi)
} func main() {
ret := zhoulin.Add(10, 20)
fmt.Println(ret)
}

执行过程和结果

// 先编译包,也可以直接执行main.go文件(go run main.go)
go build // 在执行被编译的文件
./11import_demo // 执行结果
被导入的时候,我是自动执行的...
自动执行!
100 3.14
30

包中init函数执行的时机

fmt包

package main

import "fmt"

/*
fmt包主要分为 向外输出内容 和 获取输入内容 两大部分
向外输出内容:
终端输出:Print系列 Print、Printf、Println
文件输出:Fprint系列 Fprint、Fprintf、Fprintln函数会将内容输出到一个io.Writer接口类型的变量中,
我们通常用这个函数往文件中写入内容 Sprint系列函数会把传入的数据生成并返回一个字符串,拼接字符串
Errorf函数根据format参数生成格式化字符串并返回一个包含该字符串的错误 获取输入内容
Go语言fmt包下有fmt.Scan、fmt.Scanf、fmt.Scanln三个函数,可以在程序运行过程中从标准输入获取用户的输入
fmt.Scan
Scan从标准输入扫描文本,读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
fmt.Scanf fmt.Scanln
fmt.Scanln遇到回车就结束扫描了,这个比较常用 有时候我们想完整获取输入的内容,而输入的内容可能包含空格,这种情况下可以使用bufio包来实现
func bufioDemo() {
reader := bufio.NewReader(os.Stdin) // 从标准输入生成读对象
fmt.Print("请输入内容:")
text, _ := reader.ReadString('\n') // 读到换行
text = strings.TrimSpace(text)
fmt.Printf("%#v\n", text)
}
*/ func main() {
//fmt.Print("南山")
//fmt.Print("alnk")
//fmt.Println("------------")
//fmt.Println("南山")
//fmt.Println("alnk")
//Printf("格式化字符串", 值)
// %T :查看类型
// %d :十进制数
// %b :二进制数
// %o :八进制数
// %x :十六进制数
// %c : 字符
// %s :字符串
// %p: 指针
// %v: 值
// %f:浮点数
// %t :布尔值 //var m1 = make(map[string]int, 1)
//m1["lixiang"] = 100
//fmt.Printf("%v\n", m1) //map[lixiang:100]
//fmt.Printf("%#v\n", m1) //map[string]int{"lixiang":100} //printBaifenbi(10) //fmt.Printf("%v\n", 100)
////整数 -> 字符
//fmt.Printf("%q\n", 65) // 'A'
////浮点数 -> 复数
//fmt.Printf("%b\n", 3.1415926)
////字符串
//fmt.Printf("%q\n", "理想有理想")
//fmt.Printf("%7.3s\n", "abcdefghijk") //获取用户输入
//var s string
//fmt.Print("请输入内容:")
//fmt.Scan(&s)
//fmt.Println("用户输入的内容是:", s) //var (
// name string
// age int
// class string
//)
////fmt.Scanf("%s %d %s\n", &name, &age, &class) //获取用户输入,同一行
////fmt.Println(name, age, class)
////
//fmt.Scanln(&name, &age, &class)
//fmt.Println(name, age, class) //fmt.Printf("%b\n", 1024) //10000000000
} func printBaifenbi(num int) {
fmt.Printf("%d%%\n", num)
}

示例

package main

import (
"bufio"
"fmt"
"os"
) // 获取用户输入时如果有空格 // Scanln 如果有空格会报错
func useScan() {
var s string
fmt.Print("请输入内容:")
fmt.Scanln(&s)
fmt.Printf("你输入的内容是:%s\n", s)
} // useBufio 能处理有空格的输入
func useBufio() {
var s string
reader := bufio.NewReader(os.Stdin)
fmt.Printf("请输入内容:")
s, _ = reader.ReadString('\n')
fmt.Printf("你输入的内容是:%s", s)
} // Fprintln 往终端输入、往文件输出
func useFprintln() {
fmt.Fprintln(os.Stdout, "这是一条日志") //往终端屏幕写 fileObj, _ := os.OpenFile("./test.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
defer fileObj.Close() fmt.Fprintln(fileObj, "这是一条日志记录!") //往文件写
} func main() {
//useScan()
//useBufio()
useFprintln()
}

文件读写操作包

读取文件

package main

import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
) /*
[Go语言文件操作] -- 读取文件 文件是什么?
计算机中的文件是存储在外部介质(通常是磁盘)上的数据集合,文件分为文本文件和二进制文件 [打开和关闭文件]
os.Open()函数能够打开一个文件,返回一个*File和一个err。对得到的文件实例调用close()方法能够关闭文件 [读取文件的三种方法]
1.Read方法定义如下:
func (f *File) Read(b []byte) (n int, err error)
它接收一个字节切片,返回读取的字节数和可能的具体错误,读到文件末尾时会返回0和io.EOF 2.bufio读取文件 3.ioutil读取整个文件,如果文件很大,使用这种方法是否会导致内存飙升?
*/ // 1.第一种读取文件的方法 os.Open() file.Read()
func readFromFile1() {
fileObj, err := os.Open("./main.go") //底层调用的其实就是OpenFile函数,只不过Open函数更简单
//fileObj, err := os.OpenFile("./main.go", os.O_RDONLY, 0)
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
} // 记得关闭文件
defer fileObj.Close() // 读文件
//var tmp = make([]byte, 128) //指定读的长度 切片
var tmp [128]byte // 数组 for {
n, err := fileObj.Read(tmp[:])
if err == io.EOF {
fmt.Println("读完了")
return
}
if err != nil {
fmt.Printf("read from file failed, err:%v\n", err)
return
}
//fmt.Printf("读了%d个字节\n", n)
fmt.Printf("%s", string(tmp[:n])) //为了保持文本输出的格式,这里建议用Printf if n < 128 {
return
}
}
} // 2.bufio
// bufio是在file的基础上封装了一层API,支持更多的功能
func readFromFilebyBufio() {
// 1.打开文件
fileObj, err := os.Open("./main.go")
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
} // 2.关闭文件
defer fileObj.Close() // 3.创建一个用来从文件中读内容的对象
reader := bufio.NewReader(fileObj) // 4.循环读取文件内容
for {
line, err := reader.ReadString('\n') //这里可能会产生bug,如果一行的结尾不是\n的话
if err == io.EOF {
//fmt.Println("文件读完了!!!")
return
}
if err != nil {
fmt.Printf("read line failed, err:%v\n", err)
return
}
fmt.Print(line)
}
} // 3.ioutil读取整个文件
func readFromFileByIoutil() {
ret, err := ioutil.ReadFile("./main.go")
if err != nil {
fmt.Printf("read file failed, err:%v\n", err)
return
} fmt.Println(string(ret))
} func main() {
//readFromFile1()
//readFromFilebyBufio()
readFromFileByIoutil()
}

写入文件

package main

import (
"bufio"
"fmt"
"io/ioutil"
"os"
) /*
[文件写入操作]
os.OpenFile()函数能够以指定模式打开文件,从而实现文件写入相关功能
func OpenFile(name string, flag int, perm FileMode) (*File, error) {}
其中:
name:要打开的文件名
flag:打开文件的模式
perm:权限 模式有以下几种:
os.O_WRONLY 只写
os.O_CREATE 创建文件
os.O_RDONLY 只读
os.O_RDWR 读写
os.O_TRUNC 清空
os.O_APPEND 追加 Write和WriteString
Write:写入字节切片数据
WriteString:直接写入字符串数据
*/ // 1.os.OpenFile() 打开文件写内容
func writeDemo1() {
// 1.打开文件
fileObj, err := os.OpenFile("./test1.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) //只写,如果文件不存在则创建,如果存在则清空
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
}
// 2.关闭文件
defer fileObj.Close() // 3.write 方法写入
n, err := fileObj.Write([]byte("zhoulin mengbi le 嘛!\n"))
if err != nil {
fmt.Printf("write file failed, err:%v\n", err)
return
}
fmt.Println("n:", n) //写入了多少个字节 // 4.writeString 方法写入
n, err = fileObj.WriteString("周林解释不了!\n")
if err != nil {
fmt.Printf("write file failed, err:%v\n", err)
return
}
fmt.Println("n:", n) //写入了多少个字节
} // 2.bufio.NewWriter() 写入文件
func writeDemo2() {
// 1.打开文件
fileObj, err := os.OpenFile("./test2.txt", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) //追加写入,不存在则创建
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
} // 2.关闭文件
defer fileObj.Close() //延迟关闭文件 // 3.写入文件
wr := bufio.NewWriter(fileObj) //创建一个写的对象
wr.WriteString("hello沙河\n") //写到缓存中 WriteString
wr.Write([]byte("难受啊\n")) //写到缓存中 Write
wr.Flush() //将缓存中的内容写入文件
} // 3.ioutil.WriteFile 写入文件
func writeDemo3() {
str := "hello沙河\n"
err := ioutil.WriteFile("./test3.txt", []byte(str), 0644) //这种写入方式会清空被写入文件之前的数据
if err != nil {
fmt.Printf("write file failed, err:%v\n", err)
return
}
} func main() {
writeDemo1()
writeDemo2()
writeDemo3()
}

往文件中插入内容

package main

import (
"fmt"
"io"
"os"
) // 往文件中插入内容 func f2() {
// 1.打开要操作的文件
f, err := os.OpenFile("./test.txt", os.O_RDWR, 0644)
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
}
defer f.Close() // 2.因为没有办法直接在文件中插入内容,所以要借助一个临时文件
tmpFile, err := os.OpenFile("./test.tmp", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("create tmp file failed, err:%v\n", err)
return
}
defer tmpFile.Close() // 3.读取源文件部分内容写入临时文件
var ret [2]byte //读取了2个字节
n, err := f.Read(ret[:])
if err != nil {
fmt.Printf("read from file failed, err:%v\n", err)
return
} // 4.读取的部分源文件内容写入临时文件
tmpFile.Write(ret[:n]) // 5.写入要插入的内容到临时文件
var s []byte
s = []byte{'a', 'b', 'c'}
tmpFile.Write(s) // 6.紧接着把源文件后续的所有内容写入临时文件
var x [1024]byte
for {
n, err := f.Read(x[:])
if err == io.EOF {
tmpFile.Write(x[:n])
break
}
if err != nil {
fmt.Printf("read from file failed, err:%v\n", err)
return
}
tmpFile.Write(x[:n])
} // 7.把临时文件改名为源文件
os.Rename("./test.tmp", "./test.txt") } func main() {
f2()
}

获取文件基本信息

package main

import (
"fmt"
"os"
) // 获取一个文件的基本信息 如文件大小、文件名称 // f1 获取文件详细信息,例如文件大小、名字等
func f1() {
// 1.打开文件
fileObj, err := os.Open("main.go")
if err != nil {
fmt.Printf("open file failed. err:%s\n", err)
return
}
fmt.Printf("%T\n", fileObj) //文件对象的类型 *os.File指针 // 2.获取文件对象的详细信息
fileInfo, err := fileObj.Stat()
if err != nil {
fmt.Printf("get file info failed, err:%s\n", err)
return
}
fmt.Printf("文件大小是:[%d]B\n", fileInfo.Size()) //文件大小是:[601]B
fmt.Printf("文件名称是:[%s]\n", fileInfo.Name()) //文件名称是:[main.go] 只会获取文件名称,不会获取文件路径
} func main() {
f1()
}

案例

package main

import (
"fmt"
"io"
"os"
) // 借助io.copy() 实现一个拷贝文件函数 // CopyFile拷贝文件函数
func CopyFile(dstName, srcName string) (wrtten int64, err error) {
// 以读方式打开
src, err := os.Open(srcName)
if err != nil {
fmt.Printf("open %s failed, err:%v\n", srcName, err)
return
}
defer src.Close() // 以写|创建的方式打开目标文件
dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644) //没有os.O_APPEND 会清空之前的文件
if err != nil {
fmt.Printf("open %s failed, err:%v\n", dstName, err)
return
}
defer dst.Close() return io.Copy(dst, src) //调用io.Copy()拷贝内容
} func main() {
_, err := CopyFile("./dst.txt", "./src.txt")
if err != nil {
fmt.Println("copy file failed, err:", err)
return
} fmt.Println("copy done.")
}

time包

package main

import (
"fmt"
"time"
) // time时间包 func f1() {
now := time.Now() //获取当前时间
fmt.Println(now) //2020-08-19 14:37:25.136076 +0800 CST m=+0.000102652
fmt.Println(now.Year()) //2020
fmt.Println(now.Month()) //August
fmt.Println(now.Day()) //19
fmt.Println(now.Hour()) //14
fmt.Println(now.Minute()) //39
fmt.Println(now.Second()) //30
fmt.Println(now.Date()) //日期 2020 August 19 // 时间戳
fmt.Println(now.Unix()) //1597819214 获取当前秒
fmt.Println(now.UnixNano()) //1597819255919494000 纳秒时间戳 // time.Unix() 将时间戳转为时间格式
ret := time.Unix(1597819214, 0)
fmt.Println(ret) //2020-08-19 14:40:14 +0800 CST
ret1 := time.Unix(1597819214, 484199000) //精确到纳秒
fmt.Println(ret1) //2020-08-19 14:40:14.484199 +0800 CST
fmt.Println(ret1.Year()) //2020
fmt.Println(ret1.Month()) //August
fmt.Println(ret1.Day()) //19
fmt.Println(ret1.Date()) //日期 2020 August 19 // 时间间隔
fmt.Println(time.Second) //1s
fmt.Println(time.Second * 10) //10s // 定时器
//timer := time.Tick(5 * time.Second) //间隔5s
////timer := time.Tick(1 * time.Second) //间隔1s
//for t := range timer {
// fmt.Println("定时器")
// fmt.Println(t) //1秒钟执行一次
//} // 格式化时间:把语言中时间对象转换成字符串类型的时间
// 时间类型有一个自带的方法Format进行格式化,需要注意的是Go语言中格式化时间模板不是常见的Y-m-d H:M:S
// 而是使用Go的诞生时间2006年1月2号15点04分05秒(记忆口诀为2006 1 2 3 4 5)
fmt.Println(now.Format("2006-01-02")) //2020-08-19
fmt.Println(now.Format("2006/01/02 15:04:05")) //2020/08/19 07:41:40
fmt.Println(now.Format("2006/01/02 03:04:05 PM")) //2020/08/19 07:43:07 AM
fmt.Println(now.Format("2006/01/02 15:04:05.000")) //2020/08/19 07:45:52.486
fmt.Println(time.Now().Format("2006-01-02 15:04:05")) //2020-08-19 15:51:08
fmt.Println(time.Now().Format("2006-01-02")) //2020-08-19 // 按照对应的格式解析字符串类型的时间
timeObj, _ := time.Parse("2006-01-02", "2019-08-03")
fmt.Println(timeObj) //2019-08-03 00:00:00 +0000 UTC
fmt.Println(timeObj.Unix()) //1564790400 // sleep
n := 5 //int
fmt.Println("开始sleep了")
time.Sleep(time.Duration(n) * time.Second)
fmt.Println("5秒钟过去了")
time.Sleep(5 * time.Second)
fmt.Println("5秒又钟过去了...")
} func main() {
f1()
}

根据时区计算时间差

import (
"fmt"
"time"
) // 根据时区计算时间差
func f2() {
now := time.Now() //本地当前时间
fmt.Println(now) //2020-08-21 11:43:49.71391 +0800 CST m=+0.000093483 // 按照东八区的时区和格式取解析一个字符串格式的时间
// 根据字符串加载时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return
} // 按照指定时区解析时间
timeObj, err := time.ParseInLocation("2006-01-02 15:04:05", "2021-03-13 14:18:00", loc)
if err != nil {
fmt.Println("ERROR:", err)
return
}
fmt.Println(timeObj) //时间对象相减
td := now.Sub(timeObj)
fmt.Println(td) //24h0m38.279607s
} func main() {
f2()
}

时间加减

// Add Sub 时间的加减
func f3() {
// Add 时间相加
now := time.Now() //获取当前时间
fmt.Println(now) //2020-08-21 10:31:43.955684 +0800 CST m=+0.000092436 // 十分钟以前
m, _ := time.ParseDuration("-10m")
m1 := now.Add(m)
fmt.Println(m1) // 8个小时以前
h, _ := time.ParseDuration("-1h")
h1 := now.Add(8 * h)
fmt.Println(h1) // 一天以前
d, _ := time.ParseDuration("-24h")
d1 := now.Add(d)
fmt.Println(d1) // 十分钟以后
mm, _ := time.ParseDuration("10m")
mm1 := now.Add(mm)
fmt.Println(mm1) // 8个小时以后
hh, _ := time.ParseDuration("1h")
hh1 := now.Add(8 * hh)
fmt.Println(hh1) // 一天以后
dd, _ := time.ParseDuration("24h")
dd1 := now.Add(dd)
fmt.Println(dd1) // Sub 计算两个时间差
subM := now.Sub(m1)
fmt.Println(subM.Minutes()) //10分钟 subH := now.Sub(h1)
fmt.Println(subH) //8h0m0s subD := now.Sub(d1)
fmt.Println(subD) //24h0m0s
}
// 计算两个string类型的时间差
func f4() {
// 计算两个固定的string类型的时间差
// 1.声明变量
t1 := "2020-08-21 11:25:00"
t2 := "2020-08-21 10:25:00" // 2.把string类型转化为time类型
var baseTime = "2006-01-02 15:04:05"
t1Time, _ := time.Parse(baseTime, t1)
t2Time, _ := time.Parse(baseTime, t2)
fmt.Println(t1Time) //2020-08-21 11:25:00 +0000 UTC
fmt.Println(t2Time) //2020-08-21 10:25:00 +0000 UTC // 3.利用sub计算时间差
sub1 := t1Time.Sub(t2Time)
sub2 := t2Time.Sub(t1Time)
fmt.Println(sub1) //1h0m0s
fmt.Println(sub2) //-1h0m0s
fmt.Println(sub1.Hours()) //1
fmt.Println(sub1.Minutes()) //60
fmt.Println(sub1.Seconds()) //3600 // 计算某个时间(string类型)和当前时间的时间差
// 注意要计算一个固定的时间串和本地时间间隔多少,解析时间字符串的时候需要把时区设置进去 // 1.声明变量
t3 := "2021-03-12 15:31:00"
// 1.1设置时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
return
}
// 1.2按照时区去解析时间
t3Time, err := time.ParseInLocation("2006-01-02 15:04:05", t3, loc)
if err != nil {
return
}
fmt.Println(t3Time) fmt.Println(t3Time) //2020-09-08 18:03:00 +0000 UTC //这里时区不对 // 2.获取当前时间
now := time.Now()
fmt.Println(now) //2020-09-09 18:06:10.040535 +0800 CST m=+0.000185949 // 3.相减
sub3 := now.Sub(t3Time) // 4
fmt.Println(sub3) //24h0m24.746951s
fmt.Println(sub3.Hours()) //24.006874153055556
fmt.Println(sub3.Minutes()) //1440.4124491833334
fmt.Println(sub3.Seconds()) //86424.746951
}

log包

package main

import (
"fmt"
"log"
"os"
"time"
) // 内置log包 func f1() {
// 1.打开文件
f, err := os.OpenFile("./test1.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //追加写入
if err != nil {
fmt.Println("err:", err)
return
} // 2.延迟关闭文件
defer f.Close() // 3.设置log输出位置,这里设置为文件,默认会输出到终端
log.SetOutput(f) //往文件输出 // 4.测试循环写入内容到日志文件
for {
log.Println("这是一条测试的日志f1") //不会打印到终端,会写入到文件
time.Sleep(time.Second * 3)
}
} func f2() {
log.Println("这是一条很普通的日志") //2020/09/10 10:16:02 这是一条很普通的日志 v := "很普通的"
log.Printf("这是一条%s日志。\n", v) //2020/09/10 10:16:02 这是一条很普通的日志。 // Fatal系列函数会在写入日志信息后调用os.Exit(1)
log.Fatalln("这是一条会触发fatal的日志") //2020/09/10 10:16:28 这是一条会触发fatal的日志 // Panic系列函数会在写入日志信息后panic。
log.Panic("这是一条会触发Panic的日志")
} func main() {
//f1()
f2()
}

runtime包

package main

import (
"fmt"
"path"
"runtime"
) // runtime.Caller(): 获取调用runtime.Caller所在函数的一些信息 func f(skip int) {
pc, file, line, ok := runtime.Caller(skip)
if !ok {
return
} funcName := runtime.FuncForPC(pc).Name()
fmt.Println(funcName) //函数名 fmt.Println(file) //文件名 全路径 fmt.Println(line) //行号 fmt.Println(path.Base(file)) //文件名,不是全路径
} func f1(skip int) {
f(1)
} func main() {
f(0) //注意这里的参数,0表示f这个函数本身调用
/*
输出结果:
main.f //函数名 main包下的f函数
/Users/lichengguo/go/src/code.oldboyedu.com/gostudy/day06/06runtime_demo/main.go //文件名 全路径
12 //行号
main.go //文件名,不是全路径
*/ f(1) //注意这里的参数 1表示函数main调用了f函数
/*
main.main
/Users/lichengguo/go/src/code.oldboyedu.com/gostudy/day06/06runtime_demo/main.go
41
main.go
*/ f1(2) //2 表示嵌套了2层函数才调用到f函数
/*
main.f1
/Users/lichengguo/go/src/code.oldboyedu.com/gostudy/day06/06runtime_demo/main.go
28
main.go
*/
}

json序列化和反序列化

package main

import (
"encoding/json"
"fmt"
) // str json 和结构体转换
// 序列化 反序列化 type person struct {
Name string `json:"name"`
Age int `json:"age"`
} func main() {
// 反序列化 str-->struct
s1 := `{"name":"周林", "age":9000}`
fmt.Printf("%T\n", s1) //string
var p person
json.Unmarshal([]byte(s1), &p)
fmt.Printf("%#v\n", p) //main.person{Name:"周林", Age:9000} // 序列化 strcut --> str
p1 := person{
Name: "保德路",
Age: 22,
}
strJson, _ := json.Marshal(&p1)
fmt.Printf("%#v\n", string(strJson)) //"{\"name\":\"保德路\",\"age\":22}"
}

strconv包

package main

import (
"fmt"
"strconv"
) /*
Go语言内置包之strconv
Go语言中strconv包实现了基本数据类型和其字符串表示的相互转换
strconv包实现了基本数据类型与其字符串表示的转换,主要有以下常用函数: Atoi()、Itoa()、parse系列、format系列、append系列
*/ func main() {
// 从字符串中解析出对应的数据
str := "10000"
ret1, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return
}
fmt.Printf("%#v %T\n", ret1, ret1) //10000 int64 // Atoi 字符串转换成int
retInt, _ := strconv.Atoi(str)
fmt.Printf("%#v %T\n", retInt, retInt) //10000 int // 从字符串中解析出布尔值
boolStr := "true"
boolValue, _ := strconv.ParseBool(boolStr)
fmt.Printf("%#v %T\n", boolValue, boolValue) //true bool // 从字符串解析出浮点数
floatStr := "1.234"
floatValue, _ := strconv.ParseFloat(floatStr, 64)
fmt.Printf("%#v %T\n", floatValue, floatValue) //1.234 float64 // 把数字转换成字符串类型
i := 97
ret2 := string(i)
fmt.Println(ret2) //a
ret3 := fmt.Sprintf("%d", i)
fmt.Printf("%#v\n", ret3) //"97"
ret4 := strconv.Itoa(i)
fmt.Printf("%#v %T\n", ret4, ret4) //"97" string
}

练习

目录结构

├── go.mod
├── logs
│   └── log.txt // 日志文件
├── main.go // 主文件
└── mylogs // 包
└── mylogs.go // 包文件

mylogs.go

package mylogs

/*
日志包
1.可以同时往终端和日志文件输入日志
2.日志分为5种级别[trace debug warning info error] 用法 ex:
//声明成全局变量,好让所有的函数都能调用
var logger = mylogs.NewLogger("info", "./logs/", "log.txt", true)
logger.Trace("这是一条Trace日志f1()")
logger.Debug("这是一条debug日志f1()...")
logger.Warning("warning日志f1()...")
logger.Info("info日志f1()")
logger.Error("错误日志f1()...")
*/ import (
"fmt"
"os"
) // Logger 定义一个日志结构体
type Logger struct {
Level string //日志等级[trace debug warning info error]
FilePath string //日志存放路径
FileName string //日志存放文件名称
Tag bool //true:往屏幕打印日志
} // NewLogger Logger结构体构造函数
// tag参数 是否往终端输出日志
func NewLogger(level string, filepath, filename string, tag bool) Logger {
return Logger{
Level: level,
FilePath: filepath,
FileName: filename,
Tag: tag,
}
} //方法
//Trace
func (l *Logger) Trace(logContent string) {
l.writeLogFile("trace", logContent)
} //Debug
func (l *Logger) Debug(logContent string) {
l.writeLogFile("debug", logContent)
} //Warning
func (l *Logger) Warning(logContent string) {
l.writeLogFile("warning", logContent)
} //Info
func (l *Logger) Info(logContent string) {
l.writeLogFile("info", logContent)
} //Error
func (l *Logger) Error(logContent string) {
l.writeLogFile("error", logContent)
} //写入日志方法
func (l *Logger) writeLogFile(lv string, logContent string) {
lvl, _ := parseLogLevel(lv)
level, ok := parseLogLevel(l.Level) if !ok {
fmt.Println("日志配置的等级不正确")
os.Exit(1)
} if level <= lvl {
l.writeFile(lv, logContent)
}
} //把日志内容写入到文件方法
func (l *Logger) writeFile(lv string, logContent string) {
filePath := l.FilePath + l.FileName //拼接文件日志路径和日志名称
logContent = fmt.Sprintf("[%s] %s", lv, logContent) //拼接日志内容 if l.Tag { //往屏幕输出日志内容
fmt.Println(logContent)
} fileObj, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil {
fmt.Println("log file not found")
os.Exit(1)
} defer fileObj.Close() fmt.Fprintln(fileObj, logContent) } //解析日志等级函数
func parseLogLevel(str1 string) (int, bool) {
switch str1 {
case "trace":
return 1, true
case "debug":
return 2, true
case "warning":
return 3, true
case "info":
return 4, true
case "error":
return 5, true
default:
return 0, false
}
}

main.go文件

package main

import (
"mylogs"
"time"
) // 声明成全局变量,好让所有的函数都能调用
var logger = mylogs.NewLogger("info", "./logs/", "log.txt", true) func f1() {
logger.Trace("这是一条Trace日志f1()")
logger.Debug("这是一条debug日志f1()...")
logger.Warning("warning日志f1()...")
logger.Info("info日志f1()")
logger.Error("错误日志f1()...")
} func f2() {
logger.Trace("这是一条Trace日志f2()")
logger.Debug("这是一条debug日志f2()...")
logger.Warning("warning日志f2()...")
logger.Info("info日志f2()")
logger.Error("错误日志f2()...")
} func main() {
for {
f1()
f2()
time.Sleep(2 * time.Second)
}
}

Golang语言系列-10-包的更多相关文章

  1. Golang语言系列-11-goroutine并发

    goroutine 并发 概念 package main import ( "fmt" "time" ) /* [Go语言中的并发编程 goroutine] [ ...

  2. Golang语言系列-13-常用内置包

    常用内置包 net/http包 http请求和响应 http服务端 main.go文件 package main import ( "fmt" "io/ioutil&qu ...

  3. Golang语言系列-01-Go语言简介和变量

    Go语言简介 Go(又称Golang)是Google开发的一种静态强类型.编译型.并发型,并具有垃圾回收功能的编程语言. 罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pi ...

  4. Golang语言系列-15-数据库

    数据库 MySQL 连接数据库 package main import ( "database/sql" "fmt" _ "github.com/go ...

  5. Golang语言系列-14-单元测试

    单元测试 字符串切割函数 package split_string import ( "fmt" "strings" ) // Split:切割字符串 // e ...

  6. Golang语言系列-08-结构体

    结构体 自定义类型和类型别名的区别 package main import "fmt" // 自定义类型和类型别名的区别 // type后面跟的是类型 type myInt int ...

  7. Golang语言系列-07-函数

    函数 函数的基本概念 package main import ( "fmt" ) // 函数 // 函数存在的意义:函数能够让代码结构更加清晰,更简洁,能够让代码复用 // 函数是 ...

  8. Golang语言系列-02-常用数据类型

    Go语言常用数据类型 Go 语言中有丰富的数据类型,除了基本的整型.浮点型.布尔型.字符串.byte/rune 之外, 还有数组.切片.函数.map.通道(channel).结构体等. Go语言的基本 ...

  9. Golang语言系列-17-Gin框架

    Gin框架 Gin框架简介 package main import ( "github.com/gin-gonic/gin" "io" "net/ht ...

随机推荐

  1. python 常见面试问题

    https://blog.csdn.net/weixin_43789195/article/details/87469096 https://blog.csdn.net/qq_42642945/art ...

  2. cordova自定义插件开发流程

    cordova自定义插件开发:1.cordova安装:npm install -g cordova2.plugman安装:npm install -g plugman3.cordova创建工程:cor ...

  3. .NET 6 Preview 6 正式发布: 关注网络开发

    微软.NET 团队的项目经理在博客上发布了.NET 6 Preview 6,  在候选发布阶段之前的倒数第二个预览版,也就是8月份还会发布一个Preview 7,9月份开始进入RC,两个候选版本将专注 ...

  4. Linux | Shell流程控制语句

    流程控制语句 简单的Shell 脚本还不能满足我们日常工作的需要要,因为他不能批量的帮我们完成工作,所以Shell引入了 if.for.while.case 4种流程控制语句来帮助我们完成工作. if ...

  5. gitlab部署及汉化

    目录 Git 简介 部署 1. 配置yum源(推荐) 2. 安装 配置GitLab 1.加载配置 2.修改git默认访问端口 gitlab.rb unicorn.rb gitlab-http.conf ...

  6. JS请求节流

    少废话,撸代码.欧耶! 1.节流器 // 对函数进行 节流 function throttle (fn, interval = 500) { let timer = null; let firstTi ...

  7. 锐捷路由器 RSR20-X-28

    学习帮助视频 Ruijie#show run | in natip nat outsideip nat insideip nat pool NAT_POOL prefix-length 24ip na ...

  8. C语言:判断整除

    if (aa%10==0)来判断 不能用if (aa/10==int(aa/10)) 判断

  9. ajax请求对返回数据data的处理

    1,ajax请求会根据响应头的返回数据类型对返回的数据data变量进行不同的处理 $.get("data/user-permission-submit-" + ddo.manipu ...

  10. EasyUI:combotree(树形下拉框)复选框选中父节点(子节点的状态也全部选中)输入框中只显示父节点的文本值

    参考: https://blog.csdn.net/weixin_43236850/article/details/100320564