VSCode: 正規表現のグルーピングを使って `$1 0` に置換したい

※ Visual Studio Code の話題です

# 結論を先に
数字とひっついてようが気にせず`$10`で置換しちゃいましょう。
いい感じに置換してくれます。

なお、ご想像の通り限界があります。（後述）



# やりたいこと
以下のようなテキストファイルがあったとします。

（PowerShellで`ls -r > out.txt`した結果の一部で、実際には数千行くらいあります）
```txt:PowerShell で ls -r > out.txt した結果の一部
    ディレクトリ: F:\Nintendo\Album


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2020/02/04      3:30                2020
d-----        2020/12/31      0:55                2019
d-----        2021/01/01     17:39                2021
d-----        2022/01/13      9:02                2022
d-----        2023/02/19     13:18                2023
```

時刻表示のケタが揃っていなくて扱いづらいです。
置換して`00:00`の表記にしましょう。


## どうやるの
とりあえず簡単にこうしてみました。

`^(d----- +20../../.. +) (\d:\d\d)`
↓
`$10$2`

…これだとグループの番号が`10`だと認識されそうですね。

が、気にせず置換してみちゃいます。

```txt:置換結果
    ディレクトリ: F:\Nintendo\Album


Mode                 LastWriteTime         Length Name                                                              
----                 -------------         ------ ----                                                              
d-----        2020/02/04     03:30                2020                                                              
d-----        2020/12/31     00:55                2019                                                              
d-----        2021/01/01     17:39                2021                                                              
d-----        2022/01/13     09:02                2022                                                              
d-----        2023/02/19     13:18                2023                                                              
```

ちゃんといい感じに`00:00`の表記に置換できました。

グループ`10`が存在しない場合、いい感じにグループ番号と文字`0`を分割してくれるようです。
わーぱちぱちー。

めでたしめでたし。

# ちょっと待って
今回は偶然グループ`10`が無かったから上手に置換できたけど…
グループ`10`があった場合どうすれば…？

試しに、無理やりこんな感じの正規表現にしてみました。

`^(d----- +20../../.. +) (\d:\d\d)(( )( )( )( )( )( )( ))`
↓
`$10$2$3`

`(`...`)`のグルーピングが合計で10個あります。
これはあくまでも例なので変なことをしていますが、グループが10以上になるのは十分あり得る話でしょう。
（タブ区切りのCSVで`([^\t]+)\t([^\t]+)\t ... \t([^\t]+)`とやったりとか…）

ひとまずさっきのファイルで置換してみます。

```txt:置換結果
    ディレクトリ: F:\Nintendo\Album


Mode                 LastWriteTime         Length Name                                                              
----                 -------------         ------ ----                                                              
 3:30                2020                                                              
 0:55                2019                                                              
d-----        2021/01/01     17:39                2021                                                              
 9:02                2022                                                              
d-----        2023/02/19     13:18                2023                                                              
```

予想通り、`$10`がグループ`10`だと認識され、ただの空白に置換されてしまいました。

グループ番号と数字を分割する方法は無いんでしょうか？
調べた範囲では方法はありませんでした…


# なんとか置換する方法
`${1}0`的な感じに分割することはできないし、名前付きキャプチャもできないみたいだしなので、一時文字列を経由して置換を2回行うしか方法は無さそうです。

`$1（＠＠＠＠＠ファイル中に存在しない文字列＃＃＃＃＃）0`
みたいな文字列にいったん変換したあと、
`（＠＠＠＠＠ファイル中に存在しない文字列＃＃＃＃＃）`
↓
`（空文字列）`
というように置換することでなんとか…


## 一般的なプログラミング言語では…
※ 注：知ってる限りです

### PHP (PCRE)
PHP(PCRE)では、`${1}0`と表記することでグループと数字を分割できます。
`(?P<name>pattern)`とすることで名前付きキャプチャも可能ですが、置換先の指定では参照できないようです。

サクラエディタ（確認済）など、Onigumoを使う正規表現なら似たような結果になると思われます。

参考：[PHP: preg_replace - Manual](https://www.php.net/manual/ja/function.preg-replace.php#refsect1-function.preg-replace-parameters)
参考：[PHP: サブパターン - Manual](https://www.php.net/manual/ja/regexp.reference.subpatterns.php)
実行結果：https://wandbox.org/permlink/D4mvwFMVrvYj4WvK

### Python
Pythonでは、`\g<1>1`と表記することでグループと数字を分割できます。
また、`(?P<name>pattern)`⇒`\g<name>`とすることで名前付きキャプチャを用いた置換も可能です。

参考：[re --- 正規表現操作 — Python 3.11.2 ドキュメント](https://docs.python.org/ja/3/library/re.html#regular-expression-syntax)
実行結果：https://wandbox.org/permlink/N9FCFQwquJfVYf2E

### JavaScript
JavaScriptでは、グループ番号と数字は分割できないようです。
その代わり、`(?<name>pattern)`⇒`$<name>`とすることで名前付きキャプチャを用いた置換が可能です。
…VSCodeはChromiumなのでJSでの正規表現に対応してくれてても良いと思うのですが…。

参考：[グループと後方参照 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Backreferences#%E7%A8%AE%E9%A1%9E)
参考：[String.prototype.replace() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace#%E5%BC%95%E6%95%B0%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AE%E6%96%87%E5%AD%97%E5%88%97%E3%81%AE%E6%8C%87%E5%AE%9A)
実行結果：https://wandbox.org/permlink/vO9418qsSl41GXdx

なお、古いブラウザでは未対応のようです。
ブラウザによってはそこそこ最近の実装みたい。
⇒ [JavaScript built-in: RegExp: Named capture groups | Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/mdn-javascript_builtins_regexp_named_capture_groups)
⇒ [RegExp - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp#%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%83%BC%E3%81%AE%E4%BA%92%E6%8F%9B%E6%80%A7)（Named capture groups の行）



# 参考ページ等
https://learn.microsoft.com/ja-jp/dotnet/standard/base-types/substitutions-in-regular-expressions