# 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)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wskdsgcf.gitbook.io/mastering-go-zh-cn/04.0/04.4/04.4.3.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
