> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/04.0/04.4/04.4.4.md).

# 04.4.4 正则匹配IPv4地址

一个**IPv4地址**被`.`号分成了四部分，每一部分都由8位二进制组成，十进制范围是0-255，即二进制的范围是`00000000`-`11111111`。

> *IPv6*的格式更加复杂，本节的代码不适用于IPv6。

本节的代码`findIPv4.go`将分为4个部分，第一部分：

> ```go
> package main
>
> import (
>    "bufio"
>    "fmt"
>    "io"
>    "net"
>    "os"
>    "path/filepath"
>    "regexp"
> )
>
> func findIp(input string) string {
>    partIp := "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])"
>    grammer := partIp+"\\."+partIp+"\\."+partIp+"\\."+partIp
>    matchMe := regexp.MustCompile(grammer)
>    return matchMe.FindString(input)
> }
> ```

`findIPv4.go`的代码相对之前的复杂一些，所以需要使用的包比较多。

这部分代码定义的正则表达式，能够匹配IPv4地址，是整个程序的核心，如果你定义的正则表达式不正确，将永远找不到你想要的IPv4地址！

代码中的正则表达式稍后我会解释，在此之前必须了解IPv4地址的结构，比如IPv4的点分十进制表示方法以及每一部分都不能超过255，只有这样你能写出恰当的正则表达式。

正则表达式`partIp`定义了在一个IPv4地址中，每一部分可能出现的情况，比如可能是250-255的三位数，也可能是200-249，100-199，或者10-99两位数，0-9的个位数，以上的情况必须都考虑在内。

`grammer`变量定义了IPv4地址的四个部分，每一部分必须匹配`partIp`。

> `findIPv4.go`可以在任意文本文件中帮助你找到IPv4地址。

如果你有特殊需求，比如要搜索特定的IP地址，直接修改`findIPv4.go`的正则表达式即可。

第二部分：

> ```go
> func main() {
>    arguments := os.Args
>    if len(arguments) < 2 {
>       fmt.Printf("usage: %s logfile\n",filepath.Base(arguments[0]))
>       os.Exit(1)
>    }
>    for _, filename := range arguments[1:] {
>       f,err := os.Open(filename)
>       if err != nil {
>          fmt.Printf("error openning file %s\n",err)
>          os.Exit(-1)
>       }
>       defer f.Close()
> ```

首先通过`os.Args`获取搜索的文件，然后检查命令行参数的长度是否符合要求。接下来通过一个for\`循环迭代处理。

第四部分代码：

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

类似于`selectColumn.go`的代码，我们使用`bufio.ReadString*()`逐行读取文本。

最后一部分：

> ```go
> ip := findIp(line)
>          trail := net.ParseIP(ip)
>          if trail.To4() == nil {
>             continue
>          } else {
>             fmt.Println(ip)
>          }
>       }
>    }
> }
> ```

对逐行读取的文本执行`findIp()`函数，`netParseIP()`会再次确保获取的是有效的IPv4地址。

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

> $ go run findIPv4.go auth.log
>
> 116.112.10.1 151.1.51.5 192.168.1.1

其实`findIPv4.go`会打印出很多重复的行（如果文件中有很多重复的IPv4地址）。我们可以配合UNIX命令对输出进一步处理，可以获得更加清晰直观的结果：

> go run findIPv4.go auth.log | sort -rn | uniq -c | sort -rn
>
> 38 191.168.1.1
>
> 22 182.12.5.1
>
> ....
>
> 9 10.18.2.64

`sort -rn`将`findIPv4.go`的输出作为输入，排序后倒序输出，`uniq -c`计算重复ip的出现次数，最后`sort -rn`按照重复ip的出现次数倒叙输出。

> 再次强调，`findIPv4.go`的核心是正则表达四的实现。如果正则表达式定义错误了，就会匹配不到你想要的数据，或者匹配到错误的数据。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
