正規表現による置換と最短一致

こんにちは。
荒れ狂う正規表現による検索を行う中で、少し前の私が知りたかったことを今の私がまとめました。


中でも躓いた

- 最長一致・最短一致
- 正規表現によってあるパターンから求めた文字を、あるパターンを残しつつ置換する方法

を、特に記そうと思いました。
順を追って説明していますが、読み飛ばしても大丈夫です。

ご指摘やアドバイスは、コメントでお気軽に頂けたら嬉しいです。


## まとめ

- 正規表現を使うと、パターンからマッチする文字列を得られる。
- 最長一致と最短一致は書けば簡単。
- 置換先のテキストで使える正規表現は$1～$9。


## 正規表現とは

**マッチする：正規表現で一致すること。**

ある文字列をマッチさせたい時、通常の文字に加えて"メタキャラクタ"と"構文"によるマッチを可能とする機能。

正規表現を使うと

```スタートゥインクルプリキュア.txt
キュアスター
キュアミルキー
キュアソレイユ
キュアセレーネ
キュアコスモ
キー
```

から、`^キ.+ー$`で

```スタートゥインクルプリキュア.txt
キュアスター
キュアミルキー
```

という風に、`「キ」で始まって`、`「ー」で終わる`、`3文字以上の行`をマッチさせることが出来ます。

他には、

```レインボースプラッシュ.txt
「 プリンセススターカラーペン！おとめ座！くるくるチャージ！ 」
```

を、`(プ.*?！).*（座.*?！）`でマッチさせて、`$1かに$2`で置換を行い

```レインボースプラッシュ.txt
「 プリンセススターカラーペン！かに座！くるくるチャージ！ 」
```

という置換をしたりすることが出来ます。

正規表現による検索は基本的に行単位で処理されます。

件の「正規表現によってあるパターンから求めた文字を、あるパターンを残しつつ置換する方法」がこれなのですが、ひとまず順番にメモしておきます。


## メタキャラクタと構文

- メタキャラクタは正規表現で使える特別な文字です。
- 構文は正規表現による表現です。

"メタキャラクタ"とは、"文字（＝Character）以上の意味を持った（≒超越した≒超＝meta）文字"です。
（）内はイメージです。

`あ`は、どうあっても`あ`にしかマッチしませんが、メタキャラクタである`.`は`あ`にも`A`にも`！`にもマッチします。
それは、`.`が**なんでもいいので1文字**という意味を持った**メタキャラクタ**だからです。

メタキャラクタは、メタではない普通の文字と組み合わせることが出来ます。
すると、

| 正規表現 | マッチする文字列 |
| :-- | :-- |
| ひ.る | ひかる ひある ひAる ひ！る ... |

と、"`ひ`で始まって、`なんでもいいので1文字`、`る`で終わる3文字"にマッチさせることが出来ます。

そして、**"`ひ`で始まって、`なんでもいいので1文字`、`る`で終わる3文字"**が、正規表現による表現、構文です。
ただし、正規表現でも「ひかる」が表現するものは「ひかる」なので、「ひかる」も構文です。
また、「.（なんでもいいので1文字）」だけでも、構文です。

総じて"正規表現"と捉えていても、大丈夫です。


### 単体で使える、ある文字をさすメタキャラクタ・構文

| 表現 | 意味 | マッチする文字 | 代替表現 |
| :--- | :--- | :--- | :--- |
| . | 任意の1文字にマッチする。 | あ or A or ! |
| \t | タブ |  |
| \r | 改行（CR）Macは多分これ。 | （改行） |
| \n | 改行（LF）Windowsはエディタによってこれの場合がある。 | （改行） |
| \r\n | 改行（CRLF）Windowsは大体これ。 | （改行） |
| \d | 全ての数字 | 0～9 | [0-9]
| \s | 垂直タブ以外の全ての空白文字 | （スペース等） | [ \t\f\r\n] |
| \w | 英数字、アンダーバー | a\~z A\~Z 0~9 _ | [a-zA-Z0-9_] |
| \D | 全ての数字以外 | 0～9以外 | [^0-9] |
| \S | 垂直タブ以外の全ての空白文字以外（全角文字は含みません） | （スペース等）以外 | [^ \t\f\r\n] |
| \W | 英数字、アンダーバー以外 | a\~z A\~Z 0~9 _以外 | [^a-zA-Z0-9_] |

`\t`や`\d`は、**2文字で1文字**の構文です。
（1文字というと誤解を招くかもしれませんが、とにかく2つで1つです。）

なお、`\`は**バックスラッシュ**で、Backspaceキーの左か、右Shiftの左で入力出来ます。
環境によって`¥`と表示されるかもしれませんが、¥でも大丈夫です。

全ての・全ての～以外、の表現で使用されている文字はそれぞれ以下の通りです。
セットで覚えると、思い出しやすいと思います。

| 表現 | 文字の意味 |
| :-- | :-- |
| \s、\S | white **s**paceのs |
| \w、\W | **w**ordのw。 |
| \d、\D | **d**igit（数字）のd。 |


### 組み合わせて使うメタキャラクタ

| 表現 | 意味 | 記述例 | マッチする文字列 |
| :--- | :--- | :--- | :--- |
| \+ | **直前の文字**が**1回以上**繰り返す文字列にマッチ。**最長一致**。 | オ**ヨ+**～ | オヨ～ オヨヨ～ オヨヨヨ～ ... |
| \* | **直前の文字**が**0回以上**繰り返す文字列にマッチ。**最長一致**。 | オ**ヨ***～ | オ～ オヨ～ オヨヨ～ オヨヨヨ～ ... |
| ? | **直前の文字**が**0または1文字**の場合にマッチ。**最長一致**。 | オ**ヨ?**～ | オ～ オヨ～ |

正規表現ではメタキャラクタによる豊かな表現ができる一方で、逆に`*`や`+`を見つける時、そのままではメタキャラクタとなって、`*`や`+`は見つけられません。
そこで、メタキャラクタの前に`\`を入力することで、続くメタキャラクタを**普通の文字**として使えるようになります。
このことを「エスケープさせる」とも言います。

| 表現 | 意味 | 記述例 | マッチする文字列 |
| :--- | :--- | :--- | :--- |
| \ | **直後のメタキャラクタ**の**正規表現的意味合いをエスケープ**させます。 | オ**\\+**ヨ～ | オ+ヨ～ |


### ある複数の文字を指定する構文

| 表現 | 意味 | 記述例 | マッチする文字列 |
| :--- | :-- | :-- | :-- |
| (...) | ()の中の文字を**文字列とする**。 | **(** cure **)** | precure -> cure |
| [...] | []の中に含まれる**いずれかの文字**とマッチする。 | **[** cure **]** | precure -> c u r e |
| [^...] | []の中に含まれる**文字以外**とマッチする。 | **[^** cure **]** | precure -> p  |
| {n}  | 直前の文字の**桁数を指定**。 | オヨ**{2}** | オヨヨ |
| {n,} | 直前の文字の**最小桁数を指定**。 | オヨ**{2,}** | オヨヨ オヨヨヨ オヨヨヨヨ... |
| {nA,nB}| 直前の文字の**最小桁数(nA)と最大桁数(nB)を指定**。 | オヨ**{2,3}** | オヨヨ オヨヨヨ |

`()`は、**置換操作を行う際にとても便利な構文**です。詳しくは下で解説しています。

`[]`を使用することで、全てのひらがなや全てのカタカナ、等をマッチさせられます。

| 表現 | マッチする文字列 |
| :-- | :-- |
| [亜-熙] | すべての漢字。 |
| [ぁ-ん] | すべてのひらがな。 |
| [ァ-ヶ] | すべてのカタカナ。 |
| [亜-熙ぁ-んァ-ヶ] | すべての漢字、ひらがな、カタカナ。 |


### 特定の位置を指定する構文

1行ずつ、プリキュアメンバーの名前が書かれています。

```スマイルプリキュア.txt
キュアハッピー
キュアサニー
キュアピース
キュアマーチ
キュアビューティー
```

| 表現 | 意味 | 記述例 | マッチする文字列 |
| :--- | :-- | :-- | :-- |
| ^ | **直後の文字**が**行頭**にある場合にマッチ。 | ^キ | **キ**ュアハッピー **キ**ュアサニー **キ**ュアピース **キ**ュアマーチ **キ**ュアビューティー |
| $ | **直前の文字が行末**にある場合にマッチ。 | ー$ | キュアハッピ**ー** キュアサニ**ー** キュアビューティ**ー** |


## 最長一致と最短一致について

本題その1です。
多くの正規表現はそのまま使うと**最長一致**の文字列を選択します。

例えば以下のテキストがあるとします。

```text:スタートゥインクルプリキュア
キュアスター キュアミルキー
```

ここで、**「キ」で始まって「ー」で終わる文字列**を検索するために、`キ.*ー`で検索した場合、

```text:スタートゥインクルプリキュア
キュアスター キュアミルキー
↓
「キュアスター キュアミルキー」
```

「」中がマッチする文字列です。
これは**「キ」で始まって（）「ー」で終わる文字列**という表現とマッチしているためです。

ですが、本当は**「キ」で始まって「ー」で終わるメンバー**単位でマッチさせたかった場合、`キ.*?ー`で検索すると、マッチさせることが出来ます。

```text:スタートゥインクルプリキュア
キュアスター キュアミルキー
↓
「キュアスター」 「キュアミルキー」
```

この、正規表現で**解釈可能な最短の文字列**にマッチさせるのが、最短一致です。


### 最短一致の文字列をマッチさせる構文

```text:スタートゥインクルプリキュア
キュアスター キュアミルキー
```

| 表現 | 意味 | 記述例 | マッチする文字列 |
| :--- | :--- | :--- | :--- |
| \+? | **直前の文字**が**1回以上**繰り返す文字列にマッチ。**最短一致。** | キュ.**+?**ー | キュアスター キュアミルキー キュアー |
| \*? | **直前の文字**が**0回以上**繰り返す文字列にマッチ。**最短一致。** | キュ.**\*?**ー | キュアスター キュアミルキー キュー |
| ?? | **直前の文字**が**0または1文字**の場合にマッチ。**最短一致。** | キュ**??**ー | キュー キー |


## 置換先の文字列で使える正規表現

本題その2です。

```text:おとめ座レインボースプラッシュ！
「 プリンセススターカラーペン！おとめ座！くるくるチャージ！ 」
→「 (プリンセススターカラーペン！).*(座！くるくるチャージ！) 」
```

→置換操作にて、置換文字列に`$1かに$2`を入力し、実行。

```text:かに座レインボースプラッシュ！
→「 プリンセススターカラーペン！かに座！くるくるチャージ！ 」
```

| 表現 | 意味 | 記述例 | マッチする文字列 |
|:---|:---|:---|:---|
| $1\~9 | ()で囲んでおいた文字列を出力する | 上記 | **$1**：プリンセススターカラーペン！<br>**$2**：座！クルクルチャージ！ |

これを利用することで前後の文字列からマッチさせたい文字を求めつつ、
前後の文字列に影響を与えることなくマッチさせた文字の置換が行えます。


## おわりに

長かったですね、すみません。
次はもっと誰か、または少し前……今の私の役に立つ投稿がしたいです。


## 参考文献・参考にしたページ

いつもお世話になっています。
どうもありがとうございます。

正規表現（RegExp） - とほほのWWW入門
https://www.tohoho-web.com/js/regexp.htm

正規表現：最短一致でマッチさせる表現 | WWWクリエイターズ
https://www-creators.com/archives/1804