Visual Studioで複数行検索、複数行置換を行うための手順

最近C#の開発を行うことがあり、大量のソースコードを修正する機会があった。
手作業での修正はあまりに手間でミスも起こしかねないため、Visual Studioでソースコードを正規表現によって一括置換する方法を調べた。
以下、手順をまとめておく。

#ポイント
・正規表現を使ったあいまい検索により、類似箇所の一括置換を可能とする。
・複数行の検索＆複数行への置換を可能とする。
・検索文字列と置換文字列の作成をできるだけ短時間で行う。（サクラエディタマクロを使用）

#手順
##１．Visual Studioにて検索対象（ソリューション、プロジェクト、ファイル等）を開く
ちなみに、私が今回使ったのはVisual Studio Express 2015

##２．検索文字列の作成
####（１）検索対象のソースをコピー
サクラエディタを開き、検索対象のソースを貼り付ける。

```txt:サクラエディタ（例）
            for(int tryCount = 1; ; tryCount++)
            {
                if (tryCount > 5) { throw new Exception(); }
                var driver = new InternetExplorerDriver();
                try
                {
                    new TestCases.TestCase001().Exec(driver);

                    driver.Close();
                    break;
                }
                catch
                {
                    driver.Close();
                }
            }
```


####（２）記号のエスケープ＆インデントの正規表現化
以下のサクラエディタマクロを実行する。

※サクラエディタマクロの実行方法は以下を参照
http://sakura.qp.land.to/?Macro%2F%A5%DE%A5%AF%A5%ED%A5%D5%A5%A1%A5%A4%A5%EB%A4%CE%BC%C2%B9%D4#if1cfabc

```regexp1.mac
// インデントの削除
S_ReplaceAll('^\\ *', '', 60);

// 記号のエスケープ
S_ReplaceAll('([\\(|\\)|\\.|\\*|\\+|\\?|\\^|\\\\|\\[|\\]|\\||\\{|\\}|\\ |])', '\\\\$1', 60);

// インデントの正規表現化（インデントの違いを吸収する為）
S_ReplaceAll('^', '^\\\\ *', 60);

// サクラエディタの再描画
S_ReDraw();
```

以下の様になる。

```txt:サクラエディタ（例）
^\ *for\(int\ tryCount\ =\ 1;\ ;\ tryCount\+\+\)
^\ *\{
^\ *if\ \(tryCount\ >\ 5\)\ \{\ throw\ new\ Exception\(\);\ \}
^\ *var\ driver\ =\ new\ InternetExplorerDriver\(\);
^\ *try
^\ *\{
^\ *new\ TestCases\.TestCase001\(\)\.Exec\(driver\);
^\ *
^\ *driver\.Close\(\);
^\ *break;
^\ *\}
^\ *catch
^\ *\{
^\ *driver\.Close\(\);
^\ *\}
^\ *\}
```

####（３）正規表現の編集
キャプチャしたい文字列を(.+)に変更する等、目的に応じて正規表現を編集する。

※Visual Studioの正規表現ルールについては以下を参照
https://msdn.microsoft.com/ja-jp/library/2k3te2cs.aspx

例えば、以下のように「InternetExplorerDriver」と「TestCases\.TestCase001」を
「 (.+)」へ変更してみた。

```txt:サクラエディタ（例）
^\ *for\(int\ tryCount\ =\ 1;\ ;\ tryCount\+\+\)
^\ *\{
^\ *if\ \(tryCount\ >\ 5\)\ \{\ throw\ new\ Exception\(\);\ \}
^\ *var\ driver\ =\ new\ (.+)\(\);
^\ *try
^\ *\{
^\ *new\ (.+)\(\)\.Exec\(driver\);
^\ *
^\ *driver\.Close\(\);
^\ *break;
^\ *\}
^\ *catch
^\ *\{
^\ *driver\.Close\(\);
^\ *\}
^\ *\}
```

####（４）改行コードの正規表現化（検索対象が複数行の場合）
以下のサクラエディタマクロを実行する。

```regexp2.mac
// 改行コードの正規表現化
S_ReplaceAll('\\r?\\n', '\\\\r?\\\\n', 60);

// サクラエディタの再描画
S_ReDraw();
```

以下の様になる。

```txt:サクラエディタ（例）
^\ *for\(int\ tryCount\ =\ 1;\ ;\ tryCount\+\+\)\r?\n^\ *\{\r?\n^\ *if\ \(tryCount\ >\ 5\)\ \{\ throw\ new\ Exception\(\);\ \}\r?\n^\ *var\ driver\ =\ new\ (.+)\(\);\r?\n^\ *try\r?\n^\ *\{\r?\n^\ *new\ (.+)\(\)\.Exec\(driver\);\r?\n^\ *\r?\n^\ *driver\.Close\(\);\r?\n^\ *break;\r?\n^\ *\}\r?\n^\ *catch\r?\n^\ *\{\r?\n^\ *driver\.Close\(\);\r?\n^\ *\}\r?\n^\ *\}\r?\n
```

##３．置換文字列の作成
####（１）置換文字列をコピーし、サクラエディタに貼り付ける

```txt:サクラエディタ（例）
var driver = new InternetExplorerDriver();
ExecTestCase(driver,TestCases.TestCase001);
```

####（２）正規表現の編集
検索結果から$1,$2...で文字列をする等、目的に応じて正規表現を編集する。
例えば以下の通り。

```txt:サクラエディタ（例）
var driver = new $1();
ExecTestCase(driver,$2);
```

####（３）改行コードの正規表現化

以下のサクラエディタマクロを実行する。（置換文字列が複数行の場合）

```regexp3.mac
// 改行コードの正規表現化
S_ReplaceAll('\\r?\\n', '\\\\r\\\\n', 60);

// サクラエディタの再描画
S_ReDraw();
```

以下の様になる。

```txt:サクラエディタ（例）
var driver = new $1();\r\nExecTestCase(driver,$2);\r\n
```

##４．置換の実行
Visual Studioにて、作成した検索文字列、置換文字列を使用し正規表現にて置換を行う。

####（例１）

```cs:置換前
            for(int tryCount = 1; ; tryCount++)
            {
                if (tryCount > 5) { throw new Exception(); }
                var driver = new InternetExplorerDriver();
                try
                {
                    new TestCases.TestCase001().Exec(driver);

                    driver.Close();
                    break;
                }
                catch
                {
                    driver.Close();
                }
            }
```

```cs:置換後
var driver = new InternetExplorerDriver();
ExecTestCase(driver,TestCases.TestCase001);
```

####（例２）

```cs:置換前
            for(int tryCount = 1; ; tryCount++)
            {
                if (tryCount > 5) { throw new Exception(); }
                var driver = new ChromeDriver();
                try
                {
                    new TestCases.TestCase002().Exec(driver);

                    driver.Close();
                    break;
                }
                catch
                {
                    driver.Close();
                }
            }
```

```cs:置換後
var driver = new ChromeDriver();
ExecTestCase(driver,TestCases.TestCase002);
```

#まとめ
手作業は嫌いなので、できるだけ自動化して楽がしたいです。