04.4.3 高级的正则表达式示例

本节你将学习到如何在Apache web服务器的日志文件中匹配特定格式的时间与日期字符串。同时,你也会了解到将不同格式的时间与日期写入日志文件中。与上一节一样,我们需要逐行读取Apache日志文件。

本节的代码changeDT.go将分为4部分展示,可以发现changeDT.go是第三章中timeDate.go的升级版本,只不过是使用两个正则表达式匹配不同的时间与日期。

要注意的是不要试图在程序的第一个版本就达到尽善尽美,最好的方法就是小版本快速迭代。

第一部分代码:

package main

import (
   "bufio"
   "fmt"
   "io"
   "os"
   "regexp"
   "strings"
   "time"
)

func main() {
   arguments := os.Args
   if len(arguments) ==1 {
      fmt.Println("Please provide one text file to process")
      os.Exit(1)
   }
   fileName := arguments[1]
   f, err := os.Open(fileName)
   if err != nil {
      fmt.Printf("error opening file %s ",err)
      os.Exit(1)
   }
   defer f.Close()

   notAmatch := 0
   r := bufio.NewReader(f)
   for{
      line, err := r.ReadString('\n')
      if err == io.EOF {
         break
      } else if err != nil{
         fmt.Printf("error reading file %s,",err)

      }

首先我们要打开要读取的文件,并逐行读取其内容。变量notAmatch存储输入文件中不匹配两个正则表达式的条目。

第三部分代码:

r1 := regexp.MustCompile(`.*\[(\d\d\/\w+/\d\d\d\d:\d\d:\d\d:\d\d.*)\] .*`)
if r1.MatchString(line) {
   match := r1.FindStringSubmatch(line)
   d1, err := time.Parse("02/Jan/2006:15:04:05 -0700", match[1])
   if err == nil {
      newFormat := d1.Format(time.Stamp)
      fmt.Printf(strings.Replace(line,match[1],newFormat,1))
   } else {
      notAmatch++
      continue
   }

}

可以看出只要代码执行到if中,就会执行continue,这意味着程序会继续执行本代码块中的逻辑。第一个正则表达式会匹配格式为21/Nov/2017:19:28:09 +0200的时间与日期字符串。

函数regexp.MustCompile()regex.Compile()作用相同,只不过在解析失败时会触发panic。这种情况下你只能实现一种匹配,所以就要使用regexp.FindStringSubmatch()

第三部分代码:

r2 := regexp.MustCompile(`.*\[(\w+\-\d\d-\d\d:\d\d:\d\d:\d\d.*)\] .*`)
   if r2.MatchString(line) {
      match := r2.FindStringSubmatch(line)
      d1, err := time.Parse("Jan-02-06:15:04:05 -0700", match[1])
      if err == nil {
         newFormat := d1.Format(time.Stamp)
         fmt.Print(strings.Replace(line, match[1], newFormat, 1))
      } else {
         notAmatch++
      }
      continue
   }
}

匹配的第二种时间格式是Jun-21-17:19:28:09 +0200,尽管本程序实现了两种格式的匹配,但当你掌握正则表达式之后,你可以实现任意形式的匹配。

最后一部分代码将会打印出日志中没有匹配的条目数量:

fmt.Println(notAmatch, "lines did not match!")
}

执行ChangDT.go后得到下面的输出:

$ go run changDT.go

Last updated