グローバルIPアドレス（CIDRつき）にマッチする正規表現

# はじめに
グローバルIPアドレスを203.0.113.0/24（CIDR/16～/32の範囲）といった表記で収集するにあたり
正規表現を用意したので、記事にまとめてみようと思います。

https://qiita.com/aikamanami11/items/aef0b7046d3b44efd681

上記神記事のプラスアルファ版かなと思いますが、参考になればうれしいです。


# 結論
どうぞ
```
^(((([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}((([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/32)|(([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])/31)|(([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)/30)|((0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)/29)|((0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)/28)|((0|32|64|96|128|160|192|224)/27)|((0|64|128|192)/26)|((0|128)/25)|(0/24)))|((([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}((([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])\.0/23)|(([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)\.0/22)|((0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)\.0/21)|((0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)\.0/20)|((0|32|64|96|128|160|192|224)\.0/19)|((0|64|128|192)\.0/18)|((0|128)\.0/17)|(0\.0/16))))$
```

(　)の呼応状態がはんぱないですが…
以下解説していきたいと思います。

# 解説

## メタ文字
まずこの記事で取り扱うメタ文字について軽く触れます。
| メタ文字 | 意味 |
|:-|:-|
| ～\|～  | \|の左右の文字列のいずれか（～または～）  |
| [～]  | ～のいずれか1文字  |
| (～)  |  パターンのグループ |
|  ? |  直前のパターンの0～1回繰り返し（最長一致） |
| {num}  |  直前のパターンのnum回繰り返し |
| \.  | .(ドット)のエスケープ表記 |
| ^  | 論理行頭 |
| $  | 論理文末 |	

## 解説の前に

今回の記事では、大きく分けて
・/32～/24の判定ロジック
・/23～/16の判定ロジック
と２つのグループに分けてIPアドレスの正規表現を用意しました。
分解して解説を試みます。

以下項番号は画像のNo列と呼応する形で記載していきます。
![IPアドレス.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1297607/654e8d56-81a9-5242-441d-32d4b5a59d96.png)



## /32～/24の判定ロジック
### 1)第3オクテットまでが「x.x.x.」表記になっているか判定

`(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}`
のうち
(`([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`\\.){3}
は以下いずれかの条件に当てはまるものを表現しています。(登場順に箇条書き）
* `[1-9]?[0-9]`：【十の位が1～9で一の位が0～9】もしくは【一の位が0～9】※
* `1[0-9]{2}`：【百の位が1で十の位が0～9で一の位が0～9】
* `2[0-4][0-9]`：【百の位が2で十の位が0～4で一の位が0～9】
* `25[0-5]`：【百の位が2で十の位が5で一の位が0~5】

※?は前の文字([1-9])がないか、1文字だけあることを指すので「もしくは」と表記しました。

`(`([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`\.){3}`
外側は上記の数字と「.」を3回繰り返すことを表現しています。

つまり、
`(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}` は
`【0～255の数字】.【0～255の数字】.【0～255の数字】.` を表記しています。

### 2)第4オクテット以降が/32用表記になっているか判定
`(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/32)`
のうち
(`([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`/32)
部分は先ほど述べたものと同じ【0～255の数字】を表しています。
`(`([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`/32)`
外側は【0～255の数字】の後に【/32】が続き、ひとつのグループを形成します。


### 3)第4オクテット以降が/31用表記になっているか
`(([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])/31)`
のうち
(`([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])`/31)
は以下いずれかの条件に当てはまるものを表現しています。(登場順に箇条書き）
* `[1-9]?[02468]`：【十の位が1～9で一の位が0or2or4or6or8】もしくは【一の位が0or2or4or6or8】
* `1[0-9][02468]`：【百の位が1で十の位が0～9で一の位が0or2or4or6or8】
* `2[0-4][02468]`：【百の位が2で十の位が0～4で一の位が0or2or4or6or8】
* `25[024]`：【百の位が2で十の位が5で一の位が0or2or4】


つまり0及び254以下の2の倍数を表しています。

`(`([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])`/31)`
外側は【0及び254以下の2の倍数】の後に【/31】が続き、ひとつのグループを形成します。


### 4)第4オクテット以降が/30用表記になっているか
`(([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)/30)`
のうち
(`([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)`/30)
は以下いずれかの条件に当てはまるものを表現しています。(登場順に箇条書き）
* `[2468]?[048]`：【十の位が2or4or6or8で一の位が0or4or8】もしくは【一の位が0or4or8】
* `1[02468][048]`：【百の位が1で十の位が0or2or4or6or8で一の位が0or4or8】
* `1?[13579][26]`：【百の位が1で十の位が奇数で一の位が2or6】もしくは【十の位が奇数で一の位が2or6】
* `2[024][048]`：【百の位が2で十の位が0or2or4・一の位が0or4or8】
* `2[13][26]`：【百の位が2で十の位が1or3で一の位が2or6】
* `252`：252

つまり0及び252以下の4の倍数を表しています。

`(`([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)`/30)`
外側は【0及び252以下の4の倍数】の後に【/30】が続き、ひとつのグループを形成します。


### 5)第4オクテット以降が/29用表記になっているか
2の倍数、4の倍数ときました。はい、次は8の倍数です。表記をまとめにくかったのでべた書きが多めです。

`((0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)/29)`
のうち
(`(0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)`/29)

は0及び248以下の8の倍数を表しています。
16・24・32・40・48及び216・224・232・240・248は
`2?16|2?24|2?32|2?40|2?48` と表記することでまとめました。

`(`(0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)`/29)`
外側は【0及び248以下の8の倍数】の後に【/29】が続き、ひとつのグループを形成します。


### 6)第4オクテット以降が/28用表記になっているか

`((0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)/28)`
のうち
(`(0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)`/28)
は0及び240以下の16の倍数を|でつないで表記しています。
`(`(0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)`/28)`
外側は【0及び240以下の16の倍数】の後に【/28】が続き、ひとつのグループを形成します。

### 7)第4オクテット以降が/27用表記になっているか
`((0|32|64|96|128|160|192|224)/27)`
のうち
(`(0|32|64|96|128|160|192|224)`/27)
は0及び224以下の32の倍数を|でつないで表記しています。
`(`(0|32|64|96|128|160|192|224)`/27)`
外側は【0及び224以下の32の倍数】の後に【/27】が続き、ひとつのグループを形成します。

### 8)第4オクテット以降が/26用表記になっているか
`((0|64|128|192)/26)`
のうち
(`(0|64|128|192)`/26)
は0及び192以下の64の倍数を|でつないで表記しています。
`(`(0|64|128|192)`/26)`
外側は【0及び224以下の32の倍数】の後に【/26】が続き、ひとつのグループを形成します。

### 9)第4オクテット以降が/25用表記になっているか
((0|128)/25)
は【0か128】の後に【/25】が続き、ひとつのグループを形成します。

### 10)第4オクテット以降が/24用表記になっているか
(0/24)
は0の後に【/24】が続き、ひとつのグループを形成します。

### /32～/24の判定ロジックまとめ
ここまででみてきたものをすべてつなげると以下のようになります。
（見やすさのために改行とタブ文字が含まれます。利用される際は消してください）
1~10で見てきた第4オクテット部分を|でつないでその前後を()で囲むことで、
/32～/24のいずれかの表記に合致する第4オクテットを表しました。

```
(
	(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}		
	(
			(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/32)
		|	(([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])/31)
		|	(([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)/30)
		|	((0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)/29)
		|	((0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)/28)
		|	((0|32|64|96|128|160|192|224)/27)
		|	((0|64|128|192)/26)
		|	((0|128)/25)
		|	(0/24)
	)
)		
```

## /23～/16の判定ロジック
/23～/16のIPアドレスは
* 第1~2オクテットは`([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])`
* 第3オクテット部分が <strong> /31～/24 </strong> 表記時（※）の第4オクテット部分と同表記
* 第4オクテット部分は0で固定

です。
（※/32の第4オクテット部分`([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])` はここでは登場しません）
そのため、以下のような表記になります。

```
(			
	(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}		
	(		
			(([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])\.0/23)
		|	(([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)\.0/22)
		|	((0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)\.0/21)
		|	((0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)\.0/20)
		|	((0|32|64|96|128|160|192|224)\.0/19)
		|	((0|64|128|192)\.0/18)
		|	((0|128)\.0/17)
		|	(0\.0/16)
	)		
)			
```

## まとめ

/32～/24のIPアドレスと/23～/16のIPアドレスを|でつないでその前後を()で囲むことで、
1つのIPアドレスを形成することができました。
そしてその前後を^と$で囲むことでIPアドレス以外の文字列を含まない形となります。
```
^(				
	(			
		(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}		
		(		
				(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/32)
			|	(([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])/31)
			|	(([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)/30)
			|	((0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)/29)
			|	((0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)/28)
			|	((0|32|64|96|128|160|192|224)/27)
			|	((0|64|128|192)/26)
			|	((0|128)/25)
			|	(0/24)
		)		
	)|			
	(			
		(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}		
		(		
				(([1-9]?[02468]|1[0-9][02468]|2[0-4][02468]|25[024])\.0/23)
			|	(([2468]?[048]|1[02468][048]|1?[13579][26]|2[024][048]|2[13][26]|252)\.0/22)
			|	((0|8|2?16|2?24|2?32|2?40|2?48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208)\.0/21)
			|	((0|16|32|48|64|80|96|112|128|144|160|176|192|208|224|240)\.0/20)
			|	((0|32|64|96|128|160|192|224)\.0/19)
			|	((0|64|128|192)\.0/18)
			|	((0|128)\.0/17)
			|	(0\.0/16)
		)		
	)			
)$
```

おつかれさまでした！

# 参考記事

https://qiita.com/aikamanami11/items/aef0b7046d3b44efd681
https://qiita.com/Targoyle/items/1c5454a41ea4519b0c5f
https://dotiga.software/misc/misc-others/2022-example-addresses-for-documents


# 編集後記
4の倍数を正規表現するのがいちばん楽しかったです。
この表記を用意することで`203.0.113.5/24`といった『結局`203.0.113.0/24`なの？`203.0.113.5/32`なの？どっち？』みたいな確認が減りました。
とはいえこの記事書きながらもう少しコンパクト化できそうな気がしてきました。正規表現は奥深い。