前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >1.Go语言编程学习课后实践

1.Go语言编程学习课后实践

作者头像
全栈工程师修炼指南
发布2022-09-29 17:12:02
4870
发布2022-09-29 17:12:02
举报
文章被收录于专栏:全栈工程师修炼之路

[TOC]

0x01 变量类型章节

示例1.编写代码统计出字符串"为 Hello 中国 World,Go 语言 学习"中汉字的数量。

代码语言:javascript
复制
// 方式1.统计字符串中中文个数
s2 := "为 Hello 中国 World,Go 语言 学习"
res := []rune(s2)
reslen := len(res)
count := 0
for i := 0; i < reslen; i++ {
  // UTF-8 由于中文或者其它语言占用3b~4b所以排除(0~255) 英文、符号以及特殊字符
  if res[i] > 255 {
    count++
  }
}
fmt.Printf("字符串:%s (Length = %d),一共有 %d 个中文字符", s2, reslen, count)

// 方式2.利用unicode中提供的方法统计字符串中的个数
count = 0
for _, c := range s2 {
  // 判断字符是否该字符集编码,来统计中文字符
  if unicode.Is(unicode.Han, c) {
    count++
  }
}
fmt.Printf("字符串:%s (Length = %d),一共有 %d 个中文字符", s2, reslen, count)

执行结果:

代码语言:javascript
复制
字符串:为 Hello 中国 World,Go 语言 学习 (Length = 25),一共有 7 个中文字符

0x02 运算符与流程控制章节

示例1.有一堆数字,如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字?

代码语言:javascript
复制
// 查看出现一次的数字
func showsinglenumber() {
  numbers := 1234945579785321
  snumbers := fmt.Sprintf("%d", numbers)
  slength := len(snumbers)
  for i := 0; i < slength; i++ {
    // 判断其索引位置
    if strings.Index(snumbers, string(snumbers[i])) == strings.LastIndex(snumbers, string(snumbers[i])) {
      fmt.Println("只出现一次的数字 : ", string(snumbers[i])) // 注意输出时需要强制转换否则为byte类型输出Ascii值
    }
  }
}

执行结果:

代码语言:javascript
复制
只出现一次的数字 :  8

示例2.用Go语言编写一个九九乘法表

代码语言:javascript
复制
// 九九乘法表
func MultiTable() {
  for i := 1; i <= 9; i++ {
    for j := 1; j <= i; j++ {
      fmt.Printf("%d * %d = %d, ", i, j, i*j)
    }
    fmt.Println()
  }
}

WeiyiGeek.GO九九乘法表


0x03 数组章节

示例1:求数组[1, 3, 5, 7, 8]所有元素的和

代码语言:javascript
复制
func homework1() {
  // 求数组`[1, 3, 5, 7, 8]`所有元素的和
  arr := [...]int{1, 3, 5, 7, 8}
  sum := 0
  for _, v := range arr {
    sum += v
  }
  fmt.Printf("%v 元素集合之和 : %d", arr, sum)
}

执行结果:

代码语言:javascript
复制
[1 3 5 7 8] 元素集合之和 : 24

示例2.找出数组中和为指定值的两个元素的下标,比如从数组[1, 3, 5, 7, 8]中找出和为8的两个元素的下标分别为(0,3)(1,2)

代码语言:javascript
复制
func homework2() {
  // 比如从数组`[1, 3, 5, 7, 8]`中找出和为8的两个元素的下标分别为`(0,3)`和`(1,2)`。**
  arr := [...]int{1, 3, 5, 7, 8}
  for i, v := range arr {
    for j := i + 1; j < len(arr); j++ {
      if v+arr[j] == 8 {
        fmt.Printf("arr[%d] + arr[%d] = %d \n", i, j, 8)
      }
    }
  }
}

执行结果:

代码语言:javascript
复制
arr[0] + arr[3] = 8 
arr[1] + arr[2] = 8

示例3.请使用内置的sort包对数组var a = [...]int{3, 7, 8, 9, 1}进行排序(附加题,自行查资料解答)。

代码语言:javascript
复制
# 把数组变成切片
func textsort() {
  a := [...]int{3, 7, 8, 9, 1}
  fmt.Printf("a : %T , %v , ptr : %p \n", a, a, &a)            // 数组
  sort.Ints(a[:])                                              // 排序
  fmt.Printf("a[:] : %T , %v , ptr : %p \n", a[:], a[:], a[:]) // 切片
  fmt.Println("After sorted: ", a)
}

a : [5]int , [3 7 8 9 1] , ptr : 0xc0000c6000 
a[:] : []int , [1 3 7 8 9] , ptr : 0xc0000c6000 
After sorted:  [1 3 7 8 9]

0x04 切片章节

示例1.请写出下面代码的输出结果

代码语言:javascript
复制
func text() {
  var a = make([]string, 5, 10)
  for i := 0; i < 10; i++ {
    a = append(a, fmt.Sprintf("%v", i))  // 扩容 + 10 ,容量 + 10
  }
  fmt.Println(len(a), cap(a), a)
}

实际执行结果:

代码语言:javascript
复制
15 20 [0 1 2 3 4 5 6 7 8 9]

0x05 映射章节

示例1.观察下面代码,写出最终的打印结果。

代码语言:javascript
复制
func main() {
  type Map map[string][]int
  m := make(Map)
  s := []int{1, 2}
  s = append(s, 3)
  fmt.Printf("%+v\n", s)         // [ 1,2,3 ]
  m["s"] = s
  s = append(s[:1], s[2:]...)    // 关键点
  fmt.Printf("%+v\n", s)         // [ 1,3 ]
  fmt.Printf("%+v\n", m["s"])    // [ 1,3,3 ]
}

示例2.写一个程序,统计一个字符串中每个单词出现的次数。比如:”how do you do”中how=1 do=2 you=1。

代码语言:javascript
复制
func homework2() {
  var a = "how do you do"
  var splitA = strings.Split(a, " ")
  var count = make(map[string]int, len(splitA))
  // 方式1
  for _, v := range splitA {
    value, ok := count[v]
    if !ok {
      value = 1
    } else {
      value++
    }
    count[v] = value
  }
  fmt.Printf("统计 %v 字符串每个单词出现的结果: %+v \n", a, count)

  // 方式2 (非常值得学习)
  for _, w := range splitA {
    if _, ok := count[w]; !ok {
      count[w] = 1
    } else {
      count[w]++
    }
  }
  fmt.Printf("统计 %v 字符串每个单词出现的结果: %+v \n", a, count)
}

执行结果:

代码语言:javascript
复制
统计 how do you do 字符串每个单词出现的结果: map[do:2 how:1 you:1]

示例3.判断中文字符串的回文如,一行白鹭与鹭白行一

代码语言:javascript
复制
func homework1() {
  s := "a一行白鹭与鹭白行一a"
  srune := []rune(s)
  fmt.Printf("%T, Len : %d , Cap : %d \n", srune, len(srune), cap(srune))
  for i := range srune {
    if srune[i] != srune[len(srune)-1-i] {
      fmt.Printf("`%v`不是回文字符串", s)
      return
    } else if i >= len(srune)/2 {
      fmt.Printf("`%v`是回文字符串", s)
      return
    }
  }
}

执行结果:

代码语言:javascript
复制
[]int32, Len : 11 , Cap : 12
`a一行白鹭与鹭白行一a`是回文字符串

0x06 函数章节

示例1.分金币

代码语言:javascript
复制
/*
你有50枚金币,需要分配给以下几个人:Matthew,Sarah,Augustus,Heidi,Emilie,Peter,Giana,Adriano,Aaron,Elizabeth。
分配规则如下:
a. 名字中每包含1个'e'或'E'分1枚金币
b. 名字中每包含1个'i'或'I'分2枚金币
c. 名字中每包含1个'o'或'O'分3枚金币
d: 名字中每包含1个'u'或'U'分4枚金币
写一个程序,计算每个用户分到多少金币,以及最后剩余多少金币?
程序结构如下,请实现 ‘dispatchCoin’ 函数
*/
var (
  coins = 50
  users = []string{
    "Matthew", "Sarah", "Augustus", "Heidi", "Emilie", "Peter", "Giana", "Adriano", "Aaron", "Elizabeth",
  }
  distribution = make(map[string]int, len(users))
)

func dispatchCoin() int {
  // 1.遍历用户
  for _, name := range users {
    // 2.判断用户时候是否在 map 中
    _, ok := distribution[name]
    if ok {
      continue
    }
    // 3.利用匿名函数求出每个人的获得金币数
    temp := func() int {
      count := 0
      for _, crune := range name {

        // 方式1
        // c := string(crune)
        // if c == "e" || c == "E" {
        // 	count += 1
        // } else if c == "i" || c == "I" {
        // 	count += 2
        // } else if c == "o" || c == "O" {
        // 	count += 3
        // } else if c == "u" || c == "U" {
        // 	count += 4
        // }
        // 方式2(推荐)
        switch crune {
        case 'e', 'E':
          count += 1
          coins -= 1
        case 'i', 'I':
          count += 2
          coins -= 2
        case 'o', 'O':
          count += 3
          coins -= 3
        case 'u', 'U':
          count += 4
          coins -= 4
        }
      }
      return count
    }()

    // 4.将人员和金币数进行绑定
    distribution[name] = temp
  }
  // 5.输出所有人员占有的金币数
  fmt.Println(distribution)

  // 6.返回剩余金币数量
  return coins
}

func homework() {
  left := dispatchCoin()
  fmt.Println("剩下的金币数:", left)
}

执行结果:

代码语言:javascript
复制
map[Aaron:3 Adriano:5 Augustus:12 Elizabeth:4 Emilie:6 Giana:2 Heidi:5 Matthew:1 Peter:2 Sarah:0]
剩下的金币数: 10

示例2.有n个台阶,一次可以走一步或者两步,问一共有多少种走法

代码语言:javascript
复制
func ladder(step uint64) (ret uint64) {
  // 如果只有一步阶梯则只有一种走法。
  if step == 1 {
    return 1
  }
  // 如果有两步阶梯则有两种走法。
  if step == 2 {
    return 2
  }
  // 如有三步则有3种走法,所以 n 步 = (n - 1) + (n -2)
  return ladder(step-1) + ladder(step-2)
}

func demo2() {
  ret := ladder(6)
  fmt.Println("当有六步阶梯时,有", ret, "种走法!")
}

执行结果:

代码语言:javascript
复制
当有六步阶梯时,有 13 种走法!

0x07 结构体章节

示例1.请问下面代码的执行结果是什么?

代码语言:javascript
复制
type student struct {
  name string
  age  int
}

func homework1() {
  m := make(map[string]*student)
  // 后进先出
  stus := []student{
    {name: "小王", age: 18},
    {name: "娜扎", age: 23},
    {name: "大王", age: 900}, // 初始指针指向的位置
  }
  fmt.Printf("stus : %p \n", &stus)
  for _, stu := range stus {
    m[stu.name] = &stu
    fmt.Printf("for stu :%p, value : %v, Size: %d\n", &stu, stu, unsafe.Sizeof(stu.age))
  }
  for k, v := range m {
    fmt.Println(k, "=>", v.name)
  }
}

执行结果:

代码语言:javascript
复制
stus : 0xc00000c030 
for stu :0xc00000c048, value : {小王 18}, Size: 8
for stu :0xc00000c048, value : {娜扎 23}, Size: 8
for stu :0xc00000c048, value : {大王 900}, Size: 8
小王 => 大王
娜扎 => 大王
大王 => 大王

示例2.利用函数Funtion实现考生信息系统

代码语言:javascript
复制
package main

import (
  "fmt"
  "os"
)

/**	Desc: 采用函数实现考生信息的增加与删除**/
var (
  // options
  flag uint8
  // key : value
  allStudent map[int64]*Student
)

// 结构体
type Student struct {
  id   int64
  name string
  age  uint8
}

// 构造函数
func newStudent(id int64, name string, age uint8) *Student {
  return &Student{
    id:   id,
    name: name,
    age:  age,
  }
}

// 验证考生是否存在
func judgeStudent(id int64) bool {
  _, ok := allStudent[id]
  if ok {
    return true
  } else {
    return false
  }
}

// 添加考生
func addStudent() {
  var (
    id   int64
    name string
    age  uint8
  )
  fmt.Println("#请输入考生 id name age 信息以空格分割:")
  fmt.Scan(&id, &name, &age)
  if !judgeStudent(id) {
    student := newStudent(id, name, age)
    allStudent[id] = student
  } else {
    fmt.Println("该id号的考生已存在无需重复添加.")
  }
}

// 删除考生
func delStuednt(id int64) {
  if judgeStudent(id) {
    delete(allStudent, id)
    fmt.Printf("已删除考生号为 %d 的学生信息.\n", id)
  } else {
    fmt.Println("该id号的考生不存在.")
  }

}

// 显示考生
func showStudent() {
  for k, v := range allStudent {
    fmt.Printf("uid: %d 姓名: %s 年龄: %d\n", k, v.name, v.age)
  }
}

func main() {
  // Step 0.初始化存放考生信息的Map
  allStudent = make(map[int64]*Student)
  fmt.Printf("allStudent len %v", len(allStudent))
  // Step 1
  fmt.Println("欢迎使用学生信息系统简单版本")
  fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序\n\n")

  // Step 2
  for {
    fmt.Printf("功能选择: ")
    fmt.Scanln(&flag)
    fmt.Printf("#你选择第 %d 个的选项\n", flag)
    // step 3
    switch flag {
    case 1:
      addStudent()
    case 2:
      showStudent()
    case 3:
      fmt.Printf("请输入要删除的学生 uid:")
      var uid int64
      fmt.Scanln(&uid)
      delStuednt(uid)
    case 4:
      fmt.Printf("程序结束 :")
      os.Exit(1)
    default:
      fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序")
    }
  }
}

执行结果:

代码语言:javascript
复制
欢迎使用学生信息系统简单版本
功能说明: 
1.增加学生
2.查看学生
3.删除学生
4.退出程序

功能选择: 1
# 你选择第 1 个的选项
# 请输入考生 id name age 信息以空格分割:
10086 移动 25
功能选择: 1
# 你选择第 1 个的选项
# 请输入考生 id name age 信息以空格分割:
10010 联通 25
功能选择: 1 
# 你选择第 1 个的选项
# 请输入考生 id name age 信息以空格分割:
10000 典电信 26
功能选择: 2
# 你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
uid: 10010 姓名: 联通 年龄: 25
uid: 10000 姓名: 电信 年龄: 26
功能选择: 3
# 你选择第 3 个的选项
请输入要删除的学生 uid:10000
已删除考生号为 10000 的学生信息.
功能选择: 2
# 你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
uid: 10010 姓名: 联通 年龄: 25

示例3.利用结构体方法进行实现考生信息管理系统

代码语言:javascript
复制
package main
import (
  "fmt"
  "os"
)
/**	Desc: 采用结构体方法实现考生信息的增加与删除**/
var (
  // options
  flag uint8

  // key : value
  allStudent map[int64]*Student
)

// 结构体
type Student struct {
  id   int64
  name string
  age  uint8
}

// 构造函数
func newStudent(id int64, name string, age uint8) *Student {
  return &Student{
    id:   id,
    name: name,
    age:  age,
  }
}

// 验证考生是否存在
func (s *Student) judge(id int64) bool {
  _, ok := allStudent[id]
  if ok {
    return true
  } else {
    return false
  }
}

// 添加考生
func (s *Student) add() {
  var (
    id   int64
    name string
    age  uint8
  )
  fmt.Println("#请输入考生 id name age 信息以空格分割:")
  fmt.Scan(&id, &name, &age)
  if !s.judge(id) {
    student := newStudent(id, name, age)
    allStudent[id] = student
  } else {
    fmt.Println("该id号的考生已存在无需重复添加.")
  }
}

// 删除考生
func (s *Student) del(id int64) {
  if s.judge(id) {
    delete(allStudent, id)
    fmt.Printf("已删除考生号为 %d 的学生信息.\n", id)
  } else {
    fmt.Println("该id号的考生不存在.")
  }
}

// 显示考生
func (s *Student) show() {
  for k, v := range allStudent {
    fmt.Printf("uid: %d 姓名: %s 年龄: %d\n", k, v.name, v.age)
  }
}

func main() {
  // Step 0.初始化存放考生信息的Map
  allStudent = make(map[int64]*Student, 48)
  student := Student{}

  // Step 1
  fmt.Println("欢迎使用学生信息系统简单版本")
  fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序\n\n")

  // Step 2
  for {
    fmt.Printf("功能选择: ")
    fmt.Scanln(&flag)
    fmt.Printf("#你选择第 %d 个的选项\n", flag)
    // step 3
    switch flag {
    case 1:
      student.add()  // 结构体中调用add方法
    case 2:
      student.show()  // 结构体中调用show方法
    case 3:
      fmt.Printf("请输入要删除的学生 uid:")
      var uid int64
      fmt.Scanln(&uid)
      student.del(uid)  // 结构体中调用del方法
    case 4:
      fmt.Printf("程序结束 :")
      os.Exit(1)
    default:
      fmt.Printf("功能说明: \n1.增加学生\n2.查看学生\n3.删除学生\n4.退出程序")
    }
  }
}

执行结果:

代码语言:javascript
复制
功能选择: 1
#你选择第 1 个的选项
#请输入考生 id name age 信息以空格分割:
10086 移动 25
功能选择: 1
#你选择第 1 个的选项
#请输入考生 id name age 信息以空格分割:
10010 联通 24
功能选择: 2
#你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
uid: 10010 姓名: 联通 年龄: 24
功能选择: 3
#你选择第 3 个的选项
请输入要删除的学生 uid:10010
已删除考生号为 10010 的学生信息.
功能选择: 2
#你选择第 2 个的选项
uid: 10086 姓名: 移动 年龄: 25
功能选择: 4
#你选择第 4 个的选项
程序结束 :exit status 1

作业4.反转一个单链表 示例说明: 1->2->3->4->5->NULL, 5->4->3->2->1->NULL 代码示例:

代码语言:javascript
复制
package main
import "fmt"
// 链表结构体
type ListNode struct {
  val  int
  Next *ListNode
}

// 反转链表函数
func reverseList(head *ListNode) *ListNode {
  var pre *ListNode
  cur := head
  for cur != nil {
    tmp := cur.Next // 把下一个节点存放到临时变量中
    cur.Next = pre  // 首节点赋予下一指向节点(首次Nil)
    pre = cur       // 将当前节点(首次val为1的地址)赋予pre
    cur = tmp       // 将上面临时变量地址(首次val为2的地址)赋予cur
  }
  return pre // 返回反转链表
}

func main() {
  // 1.实例化链表
  head := &ListNode{
    val: 1,
    Next: &ListNode{
      val: 2,
      Next: &ListNode{
        val: 3,
        Next: &ListNode{
          val: 4,
          Next: &ListNode{
            val:  5,
            Next: nil,
          },
        },
      },
    },
  }

  // 链表实例化对象副本拷贝
  header := head
  fmt.Println("反转前首地址:", header)
  for header != nil {
    fmt.Printf("%v->", header.val)
    header = header.Next
    if header == nil {
      fmt.Printf("Nil\n")
    }
  }

  // 2.调用反转链表
  res := reverseList(head)
  fmt.Println("反转后首地址:", res)
  for res != nil {
    fmt.Printf("%v->", res.val)
    res = res.Next
    if res == nil {
      fmt.Printf("Nil\n")
    }
  }
}

执行结果:

代码语言:javascript
复制
反转前首地址: &{1 0xc000046240}
1->2->3->4->5->Nil
反转后首地址: &{5 0xc000046260}
5->4->3->2->1->Nil

0x08 接口章节

作业1.利用接口既可以求三角形的面积也能求正方形的面积

代码语言:javascript
复制
package main

import (
  "fmt"
  "math"
)

type GarphicArea interface {
  Square(float64) float64                     // 正方形
  Triangle(float64, float64, float64) float64 //三角形
}
type GarphicName struct{ Name string }

func (g *GarphicName) Square(a float64) float64 {
  // S = a^2
  return math.Pow(a, 2)
}

func (g *GarphicName) Triangle(a, b, c float64) float64 {
  //(海伦公式)(p=(a+b+c)/2) S=sqrt[p(p-a)(p-b)(p-c)]
  p := (a + b + c) / 2
  return math.Sqrt(p * (p - a) * (p - b) * (p - c))
}

func main() {
  var ga GarphicArea = &GarphicName{"正方形"}
  fmt.Println("正方形面积: ", ga.Square(4))
  ga = &GarphicName{"三角形"}
  fmt.Println("三角形面积: ", ga.Triangle(3, 4, 5))
}

执行结果:

代码语言:javascript
复制
正方形面积:  16
三角形面积:  6

0x09 包章节

作业1.利用包来封装调用位移运算符进行左移或者右移多少位,并输出其二进制表示的结果

代码语言:javascript
复制
// # weiyigeek.top/custom/pkg/bitmove.go
// # packeageName bitmove
package bitmove
import "fmt"
func Leftmove(n int, shift int) {
  fmt.Printf("%v << %v = %08b\n", n, shift, n<<shift)
}
func RightMove(n int, shift int) {
  fmt.Printf("%v >> %v = %08b\n", n, shift, n>>shift)
}

// # 主入口文件我们可使用import来导入我们自己编写的包(注意包中属性、函数、结构体、接口需要首字母大小才能被外部引用)
// # weiyigeek.top/studygo/homework/2.packeage/displacement.go
package main
import (
  "fmt"
  bitmove "weiyigeek.top/custom/pkg"
)
func main() {
  bitmove.Leftmove(4, 2)
  bitmove.RightMove(10, 2)
}

执行结果:

代码语言:javascript
复制
4 << 2 = 00010000
10 >> 2 = 00000010

0x10 Time章节

作业1.获取当前时间,格式化输出为2006/01/02 15:04:05`格式。

fmt.Println(“当前时间: “, time.Now().Format(“2006/01/02 15:04:05”)) 当前时间: 2021/09/27 15:02:31

作业2.编写程序统计一段代码的执行耗时时间,单位精确到微秒。

代码语言:javascript
复制
func demo2() {
  // 开始时间
  startTime := time.Now()

  // 从1+...+1000之和
  sum := 0
  for i := 0; i <= 1000; i++ {
    sum += i
  }
  fmt.Println("从1+...+1000之和为", sum)

  // 结束时间
  endTime := time.Now()

  fmt.Println("该段代码耗时时间为:", endTime.Sub(startTime))
}

// ==== 执行结果 ====
从1+...+1000之和为 500500
该段代码耗时时间为: 6.765µs

0x11 阶段性综合实践

日志库 程序需求分析:

  • (1) 支持向不同的地方输出日志,例如文件或者终端stdout
  • (2) 日志多级别展示,例如Debug/Trace/Info/Warning/Error/Falat
  • (3) 开发测试与线上生产定义显示级别(Info 及以上)。
  • (4) 日志出现异常时间,异常错误文件,函数及其行数。
  • (5) 日志文件切割(按照小时、天、文件大小切割)

项目代码演示:

  • (1) weiyigeek.top/custom/mlogger/consolelogger.go
代码语言:javascript
复制
package mlogger

import (
  "errors"
  "fmt"
  "path"
  "runtime"
  "strings"
  "time"
)

// 1.自定义日志库等级
type Loglevel uint8

const (
  UNKNOWN Loglevel = iota
  DEBUG
  TRACE
  INFO
  WARNING
  ERROR
  FATAL
)

// 2.解析日志等级方法
func parseLogLevel(s string) (Loglevel, error) {
  s = strings.ToUpper(s)
  switch s {
  case "DEBUG":
    return DEBUG, nil
  case "TRACE":
    return TRACE, nil
  case "INFO":
    return INFO, nil
  case "WARNING":
    return WARNING, nil
  case "ERROR":
    return ERROR, nil
  case "FATAL":
    return FATAL, nil
  default:
    // 主意不能以标点符号来结束错误信息抛出
    err := errors.New("无效的日志级别-Log Levels Error")
    return UNKNOWN, err
  }
}

// 3.ConsoleLogger结构体
type ConsoleLogger struct {
  level Loglevel
}

// 4.ConsoleLogger结构体构造方法
func NewConsoleLog(level string) ConsoleLogger {
  logLevel, err := parseLogLevel(level)
  if err != nil {
    fmt.Print(err)
  }
  // 返回ConsoleLogger结构体对象
  return ConsoleLogger{
    level: logLevel,
  }
}

// 5.解析日志等级字符串名称
func getLevelName(lv Loglevel) string {
  switch lv {
  case DEBUG:
    return "DEBUG"
  case TRACE:
    return "TRACE"
  case INFO:
    return "INFO"
  case WARNING:
    return "WARNING"
  case ERROR:
    return "ERROR"
  case FATAL:
    return "FATAL"
  default:
    return "UNKNOWN"
  }
}

// 6.获取错误的函数名 文件 以及 行号的方法
func getFileLine(skip int) (funcName, fileName string, lineNum int) {
  pc, fileName, lineNum, ok := runtime.Caller(skip)
  if !ok {
    fmt.Printf("Runtime,Caller() failed\n")
  }
  funcName = runtime.FuncForPC(pc).Name()
  fileName = path.Base(fileName)
  funcName = strings.Split(funcName, ".")[1]
  return
}

// 7.日志显示的等级(当前日志级别大传入的日志级别则显示)
func (l ConsoleLogger) enable(Loglevel Loglevel) bool {
  return Loglevel >= l.level
}

// 8.向终端打印日志的方法
func printLogs(lv, format string, custom ...interface{}) {
  // 错误信息拼接
  msg := fmt.Sprintf(format, custom...)

  // 时间&时区
  now := time.Now()
  //timezone, _ := time.LoadLocation("Asia/Shanghai")

  // 错误文件以及函数名称
  funcName, fileName, linwNum := getFileLine(2)

  fmt.Printf("[%s] [%s] [%s:%s:%d] %s \n", now.Format("2006-01-02 15:04:05"), lv, fileName, funcName, linwNum, msg)
}

// 9.各日志等级打印
// Debug
func (l ConsoleLogger) Debug(format string, custom ...interface{}) {
  // 满足等级则打印日志
  if l.enable(DEBUG) {
    printLogs("DEBUG", format, custom...)
  }
}

// Trace
func (l ConsoleLogger) Trace(format string, custom ...interface{}) {
  // 满足等级则打印日志
  if l.enable(TRACE) {
    printLogs("TRACE", format, custom...)
  }
}

// Info
func (l ConsoleLogger) Info(format string, custom ...interface{}) {
  // 满足等级则打印日志
  if l.enable(INFO) {
    printLogs("INFO", format, custom...)
  }
}

// Error
func (l ConsoleLogger) Error(format string, custom ...interface{}) {
  // 满足等级则打印日志
  if l.enable(ERROR) {
    printLogs("ERROR", format, custom...)
  }
}

// Warning
func (l ConsoleLogger) Warning(format string, custom ...interface{}) {
  // 满足等级则打印日志
  if l.enable(WARNING) {
    printLogs("WARNING", format, custom...)
  }
}

// Falat
func (l ConsoleLogger) Falat(format string, custom ...interface{}) {
  // 满足等级则打印日志
  if l.enable(FATAL) {
    printLogs("FATAL", format, custom...)
  }
}

// 方法接口定义
type Logger interface {
  Debug(format string, custom ...interface{})
  Trace(format string, custom ...interface{})
  Info(format string, custom ...interface{})
  Warning(format string, custom ...interface{})
  Error(format string, custom ...interface{})
  Falat(format string, custom ...interface{})
}

(2) weiyigeek.top/custom/mlogger/filelogger.go

代码语言:javascript
复制
package mlogger

// 主要向文件里写入日志相关信息

import (
  "fmt"
  "os"
  "path"
  "time"
)

// 1.文件记录日志结构体
type FileLogger struct {
  level       Loglevel
  filePath    string
  fileName    string
  fileObj     *os.File
  errFileObj  *os.File
  maxFileSize int64
}

// 2.文件日志构造方法
func NewFileLogger(levelStr, fp, fn string, maxSize int64) *FileLogger {
  // 解析日志等级
  Loglevel, err := parseLogLevel(levelStr)
  if err != nil {
    panic(err)
  }

  // 文件日志结构体对象
  fobj := &FileLogger{
    level:       Loglevel,
    filePath:    fp,
    fileName:    fn,
    maxFileSize: maxSize,
  }

  // 初始化文件日志文件句柄
  initErr := fobj.init()
  if initErr != nil {
    panic(initErr)
  }

  // 返回对象
  return fobj
}

// 3.文件日志显示的等级控制方法(当前日志级别大传入的日志级别则显示)
func (f FileLogger) enable(Loglevel Loglevel) bool {
  return Loglevel >= f.level
}

// 4.初始化文件日志文件句柄方法
func (f *FileLogger) init() error {
  // 通用日志名称与错误日志名称路径拼接
  fullName := path.Join(f.filePath, f.fileName) + ".logs"
  fullErrName := path.Join(f.filePath, f.fileName) + ".err.logs"

  // 创建文件句柄并
  fileObj, err := os.OpenFile(fullName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Printf("Open Logs file failed, err : %v \n", err)
    return err
  }

  errFileObj, err := os.OpenFile(fullErrName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Printf("Open Error logs file failed, err : %v \n", err)
    return err
  }

  // 赋值其对象
  f.fileObj = fileObj
  f.errFileObj = errFileObj
  return nil
}

// 5.验证文件的大小方法
func (f *FileLogger) checkFileSize(file *os.File) bool {
  // 文件信息返回的是结构体
  fileInfo, err := file.Stat()
  if err != nil {
    fmt.Printf("checkFileSize - Get file info failed, err : %v \n", err)
    return false
  }
  // 如果当前文件的大小值大于返回True,则进行切割
  if fileInfo.Size() > f.maxFileSize {
    return true
  }
  return false
}

// 6.进行日志文件的切割方法
func (f *FileLogger) SplitFile(file *os.File) (*os.File, error) {
  // 1) 获取文件句柄信息并关闭当前日志文件
  fileInfo, err := file.Stat()
  if err != nil {
    fmt.Printf("SplitFile - Get file info failed, err : %v \n", err)
    return nil, err
  }
  nowStr := time.Now().Format("2006-01-02_150405")
  LogName := path.Join(f.filePath, fileInfo.Name())
  newLogName := fmt.Sprintf("%s.bak.%s", LogName, nowStr)
  file.Close()

  // 2) 备份并重命名日志文件
  os.Rename(LogName, newLogName)

  // 3) 重新打开一个新的日志文件
  fileObj, err := os.OpenFile(LogName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  if err != nil {
    fmt.Printf("Open New Logs file failed, err : %v \n", err)
    return nil, err
  }

  // 4) 将对象进行返回
  return fileObj, nil
}

// 7.将日志分别打印进对应的文件之中
func (f *FileLogger) printLogs(level Loglevel, format string, custom ...interface{}) error {
  // 1) 判断日志等级是否满足指定等级之上的要求
  if f.enable(level) {
    // 2) 异常信息
    msg := fmt.Sprintf(format, custom...)

    // 3) 时间与时区
    now := time.Now()
    //timezone, _ := time.LoadLocation("Asia/Shanghai")

    // 4) 异常文件函数信息
    funcName, fileName, linwNum := getFileLine(2)

    // 5) 通用日志文件切割判断及输入
    if f.checkFileSize(f.fileObj) {
      f.fileObj, _ = f.SplitFile(f.fileObj)
    }
    fmt.Fprintf(f.fileObj, "[%s] [%s] [%s:%s:%d] %s \n", now.Format("2006-01-02 15:04:05"), getLevelName(level), fileName, funcName, linwNum, msg)

    // 6) 严重与错误日志文件切割判断及输入
    if level >= ERROR {
      if f.checkFileSize(f.errFileObj) {
        f.errFileObj, _ = f.SplitFile(f.errFileObj)
      }
      fmt.Fprintf(f.errFileObj, "[%s] [%s] [%s:%s:%d] %s \n", now.Format("2006-01-02 15:04:05"), getLevelName(level), fileName, funcName, linwNum, msg)
    }
  }
  return nil
}

// 8.日志等级调用分别方法
// Debug
func (f *FileLogger) Debug(format string, custom ...interface{}) {
  f.printLogs(DEBUG, format, custom...)
}

// Trace
func (f *FileLogger) Trace(format string, custom ...interface{}) {
  f.printLogs(TRACE, format, custom...)
}

// Info
func (f *FileLogger) Info(format string, custom ...interface{}) {
  f.printLogs(INFO, format, custom...)
}

// Error
func (f *FileLogger) Error(format string, custom ...interface{}) {
  f.printLogs(ERROR, format, custom...)
}

// Warning
func (f *FileLogger) Warning(format string, custom ...interface{}) {
  f.printLogs(WARNING, format, custom...)
}

// Falat
func (f *FileLogger) Falat(format string, custom ...interface{}) {
  f.printLogs(FATAL, format, custom...)
}

func (f *FileLogger) Close() {
  f.fileObj.Close()
  f.errFileObj.Close()
}

(3) main.go

代码语言:javascript
复制
/**自定义日志库调用演示入口**/
package main
import (
  "time"
  "weiyigeek.top/custom/mlogger"
)

// 示例1.向终端中进行日志信息的打印
func demo1() {
  log := mlogger.NewConsoleLog("Debug")
  for {
    log.Debug("Debug 日志: %s", "Deveploment Debug")
    log.Trace("Trace 日志: %s", "Deveploment Trace")
    log.Info("Info 日志: %s,%d", "Production Info", 1024)
    log.Warning("Warning 日志: %s", "Production Warning")
    log.Error("Error 日志: %s", "Production Error")
    log.Falat("Falat 日志: %s", "Production Falat")
    time.Sleep(time.Second * 1)
  }
}

// 示例2.将日志信息输入到文件之中
func demo2() {
  log := mlogger.NewFileLogger("INFO", "./", "weiyigeek", 5*1024)
  for {
    log.Debug("Debug 日志: %s", "FileLogger - 开发环境日志基级别 Debug")
    log.Trace("Trace 日志: %s", "FileLogger - 开发环境日志基级别 Trace")
    log.Info("Info 日志: %s,%d", "FileLogger - 生产环境日志基级别 Info", 1024)
    log.Warning("Warning 日志: %s", "FileLogger - 生产环境日志基级别 Warning")
    log.Error("Error 日志: %s", "FileLogger - 生产环境日志基级别 Error")
    log.Falat("Falat 日志: %s", "FileLogger - 生产环境日志基级别 Falat")
    time.Sleep(time.Second * 1)
  }
}

func main() {
  demo1()
  demo2()
}

运行结果:

WeiyiGeek.Logger两种实现方式

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 变量类型章节
  • 0x02 运算符与流程控制章节
  • 0x03 数组章节
  • 0x04 切片章节
  • 0x05 映射章节
  • 0x06 函数章节
  • 0x07 结构体章节
  • 0x08 接口章节
  • 0x09 包章节
  • 0x10 Time章节
  • 0x11 阶段性综合实践
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档