[正規表現] 〇〇という文字列が付かない部分を検索する(否定的先読み)

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと
正規表現で、"〇〇〇"という文字列が**つかない部分**という検索をしたいため、正規表現の「否定的先読み」を使いたい。
また、そうやって検索した文字列を置換して、消したい。

具体的にやりたいこととしては、
Qiita記事の目次を作っているのだが、記事のタイトルにその記事が何を書いてるかざっくり表すために`[WPF/xaml]`とか`【C#/WPF】`とか付けていたが、目次にして分類して目次にしたうえで記事タイトルにこれを付けるとなんかごちゃっとするので、その部分をけしたい。

# 具体的にやりたいこと

具体的には、こういう文字列から、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/7c1419d2-8c84-a771-b09a-4435f2052afc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/916e355d-073b-4706-34d0-2d81c76a14f1.png)
これを消したい。

# 現状の目次の内容

現状、目次は下記のような書き方をしている。

```before
# 目次

- **ラズパイ+WindowsIoT（UWP/C#）**
    - [RaspberryPi3でWindows10 IoT Coreを動かす(2019/02/13)](https://qiita.com/tera1707/items/51027e2896f708959eaf)
    - [[C#/WinIoT/I2C] 9軸センサ(MPU-9150)でジャイロ・加速度・磁力センサの値をとる](https://qiita.com/tera1707/items/33a8b439fddc1075f93b)
    - []()

- **画面作成(WPF/xaml)**
    - [[WPF/xaml] xaml＋C#で当番決めのためのルーレットを作る](https://qiita.com/tera1707/items/2a94c262c93b93854688)
    - [[WPF/xaml]Storyboardでアニメーションをつくる](https://qiita.com/tera1707/items/a7fcdd95fc3120ae3c8b)
    - [[WPF/xaml]Storyboardでアニメーションをつくる2(TargetPropertyの階層的な指定)](https://qiita.com/tera1707/items/ee6d72bb68a8e2131082)
    - [[WPF/C#]Prism(6.3.0)のRegionで画面遷移をする](https://qiita.com/tera1707/items/ad4437bb64077e214c5e)
    - [【C#/WPF】EventTriggerを使って、Buttonでなくてもクリック時のCommandをかけるようにする](https://qiita.com/tera1707/items/7ecde6e97a19437cbf72)
    - [【WPF】ListBoxのアイテムに、バインドしたコレクションのインデックスを出す](https://qiita.com/tera1707/items/791bdb887eae4c0ea6a4)
    - [【WPF】ItemsControlで、複数のControlを重ねて表示する](https://qiita.com/tera1707/items/47c932d84b1b706dc344)
    - [【WPF】ControlTemplateの中にViewModelのプロパティをバインドする](https://qiita.com/tera1707/items/c2e2450b823f3ca77952)
    - [【WPF】RelativeSource(バインディング)の使い方メモ](https://qiita.com/tera1707/items/73cda312b7cd9c4df40d)
    - [WPFのListBoxをカスタムする](https://qiita.com/tera1707/items/363d2a33eadcb3eb275a)
    - [簡単なユーザーコントロール(WPF)の作り方](https://qiita.com/tera1707/items/8d24b21a05ad84a1c92f)
    - [WPF画面とやり取りするための最低限の構成](https://qiita.com/tera1707/items/9114906e07cc80f6ed61)

以下略
```

# まず試したこと
**「"["と"]"で文字数不定の文字列を挟んでいる部分を検索する」**という意味で、正規表現`\[\[.*\]`で検索をすればよいと考え実施したが、下記のようになってしまった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/43e358a7-61fc-a34b-04f1-b8c272c24a0d.png)


上記の中で、"["と"]"でくくった部分の中に、さらに"["と"]"でくくった部分があるために、本当に消したい`[]`の外側の`[]`が取れてしまう。

# 解決方法(正規表現の「否定的先読み」を使う)
これではこまるので、次に、
**「外側の`[]`の後ろには、必ず`(`がつく」**というところに注目した。

これを利用して、
**「"["と"]"で文字数不定の文字列を挟んでいる部分を検索する。ただし、"]"の直後に"("が付かないものを検索する。」** という条件にしようと考えた。

ここで、正規表現の**「否定的先読み」**を使う。

# 否定的先読みの書き方

`(?!否定したい文字列)`と書く。

今回のように"["の後ろに"("がついてほしくないというときは`(?!\()`と書く。
※"("は特殊文字のため、"\"を前につける必要がある。

さらに、今回やりたい**「"["と"]"で文字数不定の文字列を挟んでいる部分を検索する。ただし、"]"の直後に"("が付かないものを検索する。」** を実現するには`\[\[.*\](?!\()`と書く。

".*"は「任意文字数の任意文字」を表す。要は「間に何文字来てもいいように」してる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/bfa90ce2-fa75-5d87-4baf-4afc384ee8ee.png)


そのようにして検索したのが下記。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/68486355-3e8b-462c-3288-ba114e9437b0.png)

これで、やりたい検索ができた。

# おまけ

あと、目次の中に"【"と"】"で挟まれた部分があり、これも消したいので`\[【.*】`で検索し、置換した。(こっちは普通にやるだけなので簡単。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/912704e6-e6cb-f3a3-78a4-dca3939f185a.png)


# 参考
否定的先読みについて
https://nymemo.com/phpcate/293/