Go语言基础(五) flag、fmt、time、file 库、日志

0、flag 库

0.1 os.Args

func fn(){
	// 这里关键是要 知道 os.Args 会把参数变成一个个字符串,然后是个切片
	if len(os.Args)>0 {
		for index, arg := range os.Args {
			fmt.Printf("args[%d]=%v \n", index,arg)
		}
	}
}

0.2 flag 参数类型

flag包支持几乎所有的go基础数据类型

0.3 flag参数的定义方法

如何定义命令行flag 参数?
方法一:

func fn(){
	// 返回的都是 指针
	name:=flag.String("name","unknown","人名")
	age := flag.Int("age",0, "age")
	// 这个 parse() 不能少,容易漏掉
	flag.Parse()
	fmt.Println(*name,*age)
}

方法二:

func fn(){
	var name string
	var age int

	flag.StringVar(&name, "name","unknown","名字")
	flag.IntVar(&age,"age",0,"年纪")

	flag.Parse()

	fmt.Println(name,age)
}

0.4 flag.parse

在定义好命令行的flag参数后,还需要flag.parse() 来解析命令行参数。flag库支持常见的的参数格式:

-flag xxx
-- flag xxx
flag xxx
-flag=xxx

一、time标准库

1.1 时间类型

采用公历时间.
先要获取时间对象,然后获取年月日 时分秒

	v := time.Now()
	v.Year()
	//...
	v.Second()

1.2 时间戳

由时间获取时间戳:

t:=time.Now()
s := t.Unix()	// unix 时间戳
fmt.Println(s)

由时间戳获取时间:

u:=time.Unix(s,0)
fmt.Println(u)

1.3 时间间隔

time.Duration是time包定义的一个类型,它代表两个时间点之间经过的时间间隔,以纳秒为单位。

fmt.Println(time.Second)	// 1s
fmt.Println(time.Minute)   // 1 min

1.4 时间操作

  • add
  • sub
  • equal
  • before
  • after

1.5 定时器

定时器本质是个channel。可以实现定期做事。倒是有点像java的Timer

t:= time.Tick(time.Second)
for i := range t {
	fmt.Println(i)
}

1.6 时间格式化

这个很有意思,一般的语言都是用时间模板如:yyyy-mm-dd ,但是go中的语言模板是使用 go 诞生的那个时间点 2006 1 2 3 4 。技术大佬的“皮” (浪漫)是别具一格的

举个栗子:

func fn(){
	t:=time.Now()
	fmt.Println(t.Format("2006-01-02 15:04:05"))	
	//2021-01-30 20:22:16
}

1.7 字符串–> 时间

t := time.Now()
fmt.Println(t)

// 加入时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
	fmt.Println(err)
	return
}

parsed, err := time.ParseInLocation("2006/01/02 15:04:05", "2010/08/04 01:00:00", loc)
if err !=nil {
	fmt.Println(err)
	return
}

fmt.Println(parsed)
fmt.Println(parsed.Sub(t))

1.8 examples

需求:编写程序统计一段代码的执行耗时时间,单位精确到微秒。
1、第一个场景

func fn(){
	f:=func ()  {
		time.Sleep(time.Second)
	}
	d:=calculateDuration(f)
	fmt.Println("duration=", d)
}

func calculateDuration(f func()) int64{
	t:=time.Now().Unix()
	f()
	d:= time.Now().Unix() - t
	return d
}

2、第二种场景

// f 是一个匿名函数
f:=func (a int,b int) int {
	fmt.Println("......")
	time.Sleep(time.Second * 2)
	return a+b
}

//不能直接把 f 作为参数传给 calculateDuration;
// 所以做了一个适配。这个地方比较绕,要仔细看看
fn := func (foo func(a,b int) int, a int, b int) func() {
	return func() {
		foo(a,b)
	}
}

d := calculateDuration(fn(f,1,2))

fmt.Println("duration=", d)
}

func calculateDuration(f func()) int64{
t:=time.Now().Unix()
f()
d:= time.Now().Unix() - t
return d
}

二、fmt标准库

2.1 输入

2.1.1 print

太基础了,不说了

2.1.2 Fprint

将内容输出到一个io.writer接口类型的变量中。一般用来向文件中写入内容

func fn3(){
	// 打开一个文件句柄
	file,ex := os.OpenFile("./test.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	fmt.Printf("%T \n",file)
	if ex !=nil {
		fmt.Println(ex)
		return
	}
	// 向打开的文件句柄中写入内容
	var s = "this is a test"
	fmt.Fprint(file,s)
}

2.1.3 Sprint

Sprint系列函数会把传入的数据生成并返回一个字符串。示例略

2.1.4 Errorf

Errorf函数根据format参数生成格式化字符串并返回一个包含该字符串的错误。

2.1.5 格式化占位符

最常用也就这几个:

  • %T --> 打印变量类型
  • %v --> 值的默认格式表示
  • %s --> 字符串占位,或者 []byte 占位
  • %t --> 布尔型占位
  • %#v --> 值的Go语法表示
  • %c --> Unicode 码值表示(其实就是 字符)
  • %b --> 二进制
  • %o --> 八进制
  • %ox --> 十六进制
  • 整型 、浮点数、复数,等等 N多,%v 一般也就够,不需要记那么多。

2.2 输出

2.2.1 fmt.Scan

示例略,见api doc即可

2.2.2 fmt.Scanf

示例略,见api doc即可

2.2.3 fmt.Scanln

示例略,见api doc即可

三、go 操作文件

3.1 打开、读取文件

举个栗子,循环读取文件内容,最终输出:

func fn(){
	f,e:=os.Open("./main.go")
	if e != nil {
		fmt.Println("failed to open file",e)
		return
	}

	defer f.Close()

	var buffer = make([]byte, 128)
	var content = make([]byte,0)
	for{
		// 读取的时候后一次会不断覆盖前一次,
		// 所以没有必要每次都 新make一个 buffer 
		num,err:=f.Read(buffer)
		if err == io.EOF {
			fmt.Println("read done")
			break
		}
		if err !=nil {
			fmt.Println("failed to read..")
			return
		}
		content=append(content, buffer[:num]...)
	}

	fmt.Println(string(content))
}

3.2 包装类 bufio

bufio 是在file基础上又封装了一层,功能更加强大

func fn(){
	f,e:=os.Open("./main.go")
	if e !=nil {
		fmt.Println("failed to read",e)
		return
	}

	defer f.Close()

	r:=bufio.NewReader(f)
	for{
	 //按行去读。注意这里的 '\n' 是单引号,表示字符, ‘\n’ 是换行符
		line,ex := r.ReadString('\n')
		if ex == io.EOF {
			// 这个地方 不能少...读下 doc 就知道,假如出现了 异常,
			// readString会在返回 error前先返回数据
			if len(line) !=0 {
				fmt.Println("line:", line)
			}
			fmt.Println("read done")
			break
		}
		if ex != nil {
			fmt.Println("failed to read:", ex)
			return
		}

		fmt.Print(line)
	}
}

3.3 ioutil

ioutil包提供了更加完备的API,比如:
ioutil.ReadFile() 能够读取整个文件

3.4 文件写入操作

3.4.1 Write和WriteString

func fn(){
	// file 可创建、清空、写入
	file,err:= os.OpenFile("./test.txt",os.O_CREATE|os.O_TRUNC|os.O_WRONLY,0666)
	if err!=nil {
		fmt.Print("failed to open file", err)
	}
	defer file.Close()

	file.Write([]byte("i love cn"))	//写入字节切片
	file.WriteString("i hate us")	//写入字符串

	content,ex := ioutil.ReadFile("./test.txt")
	if ex != nil {
		fmt.Print("failed to open file for reading", ex)
		return
	}

	fmt.Println(string(content))

}

3.4.2 bufio.NewWriter

func fn(){
	file,err:= os.OpenFile("./test.txt",os.O_CREATE|os.O_TRUNC|os.O_WRONLY,0666)
	if err!=nil {
		fmt.Print("failed to open file", err)
	}
	defer file.Close()

	w:=bufio.NewWriter(file)
	// 虽然有返回值,但是没有取
	w.WriteString("i love this world")	// 本质是将 数据 写入缓冲,而不是直接刷盘
	
	w.Flush() // 真正刷盘
}

3.4.3 ioutil.WriteFile

同样的,ioutil提供了可直接写入文件的工具类,还是很方便的

3.4.4 examples

1、借助io.Copy()实现一个拷贝文件函数。

// 实现一个copy
func copy(from string, to string) (int,error) {
	f,e:=os.OpenFile(from,os.O_RDONLY, 0666)
	if e !=nil {
		fmt.Println("failed to open 'from' file", e)
		return 0,e
	}
	defer f.Close()
	t, ex := os.OpenFile(to,os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
	if e!=nil {
		fmt.Println("failed to open 'to' file",ex)
		return 0,ex
	}
	written, err:=io.Copy(t,f)

	defer t.Close()
	return int(written), err
}

2、读写同一个文件要注意指针位置

func bar(){
	f,e:=os.OpenFile("./conf.ini",os.O_RDWR|os.O_APPEND, 0777)
	if e!=nil {
		fmt.Println("Failed to open file", e)
		return
	}

	_,ex:=f.WriteString("this is a test")
	if ex!=nil {
		fmt.Println("Failed to write in",e)
		return
	}

	var content = make([]byte,10)
	i,err:=f.Read(content)
	if err !=nil {
		// 文件已经移到了最后,所以,实际上是读不到内容的,这个地方要注意。
		// 应该首先判断 EOF 【go 中的 EOF 是个 error !】
		fmt.Println("Failed to read",err)	
		return
	}

	fmt.Println(i)
	fmt.Println(string(content))
}

四、strconv 包

这个包用来处理 字符串和数字之间的转换。API太多了,不一一写了,主要记几个特别常用的;前面三个已经能解决大部分问题:

  • strconv.Atoi
  • strconv.Itoa
  • strconv.ParseBool
  • strconv.ParseInt
  • strconv.ParseFloat
  • strconv.ParseUint
  • strconv.FormatBool
  • strconv.FormatInt

  • 这里的函数命名是沿袭了C语言,所谓的 Atoi ,其实就是 alphabet to integer ,字面翻译就是 字符串 --> 数字。这种命名,真是程序员的浪漫。

五、Log库

5.1 logger

logger提供了格式化输出的方法:

  • Print
  • Fatal
  • Panic
    可以直接通过类似log.Print来调用。这里有个有意思的地方:Fatal系列函数会在写入日志信息后调用os.Exit(1)。Panic系列函数会在写入日志信息后panic。换言之,日志行为实际上也影响了代码执行流程,不是单纯的一个记录操作。

5.2 配置logger

1、要查看和配置logger,可使用 Flags() setFlags()函数。比如:
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
其中:log.Llongfile --> 这是记录代码文件的目录
2、配置日志前缀:
log.SetPrefix("[little boy]")
3、配置日志输出位置
log.SetOutput(f)

来一个简单demo:

func main() {
	f, e := os.OpenFile("./log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0777)
	if e != nil {
		fmt.Println("create log file error", e)
		return
	}
	log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
	log.SetPrefix("[little boy]")
	log.SetOutput(f)
	for {

		log.Println("hello")
		time.Sleep(time.Second)
		// 如果这里是  Fatal() ,则有可能日志输出不出来,
		// Fatal 会导致进程退出,日志来不及刷盘(?)
		log.Println("fatal error!")
	}

}

六、自己实现一个日志库

自己搞一个日志库,要求支持:
1、往不同地方输出
2、支持日志级别
3、支持日志开关
4、输出时间、行号、文件名、级别、日志内容
5、支持切割

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页