# 04.4.3 高级的正则表达式示例

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

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

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

第一部分代码：

> ```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`存储输入文件中不匹配两个正则表达式的条目。

第三部分代码：

> ```go
> 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()`。

第三部分代码：

> ```go
> 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`，尽管本程序实现了两种格式的匹配，但当你掌握正则表达式之后，你可以实现任意形式的匹配。

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

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

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

> $ go run changDT.go

![](https://ws1.sinaimg.cn/large/006tNc79ly1fz7pr5qkanj310g05yq48.jpg)
