正規表現 vフラグの使い方

# 背景

[私が以前書いた記事で](https://qiita.com/yamatai12/items/5839d94334ed2b7f5536#:~:text=set%E8%A8%98%E6%B3%95%2B%E6%96%87%E5%AD%97%E5%88%97%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%82%92%E6%8C%81%E3%81%A4RegExp%20v%E3%83%95%E3%83%A9%E3%82%B0)この正規表現について勉強する為です。

まずuフラグについて理解する必要があるのでそちらを書きます。

# u (unicode) フラグ
ES2015 に u (unicode) フラグが導入され、コードポイント単位で正規表現を扱えるようになった。
- コードポイントとは
    [Code Point](https://jsprimer.net/basic/string-unicode/#code-point:~:text=%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84%E3%80%82-,Code%20Point,-Unicode%E3%81%AF%E3%81%99%E3%81%B9%E3%81%A6)
     >「文字」に対する「一意のID」のこと

    "A" という文字には、 U+0041 というコードポイントが割り当てられる
    ```js
    console.log('A'.codePointAt(0).toString(16))
    //41
    console.log(`\u{41}`)
    //'A'
    ```
    Code Pointは\u{Code Pointの16進数の値}のようにエスケープシーケンスとして記述できる
    
    以下の例のように、uフラグをつけることでUnicode文字を正規表現で扱えます。
    ```js
    const str = 'A';
    /\u{41}/.test(str);  // false
    /\u{41}/u.test(str); // true
    ```

# RegExp Unicode Property Escapes \p{...}, \P{...}とvフラグ

RegExp Unicode Property Escapesとは
[Unicode character class escape: \p{...}, \P{...}](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Unicode_character_class_escape)

\p{…},\P{…} を使うには、uフラグをセットで使います。
Unicode文字Propertyにどんなものがわからなかったですが、こちら参考になりました。
[Unicode Utilities: Character Properties](https://util.unicode.org/UnicodeJsps/character.jsp)

```js
// "Emoji "というUnicode文字Propertyの正規表現を定義
const re = /^\p{Emoji}$/u;

// 1つのコードポイントからなる絵文字をマッチさせる
re.test('⚽'); // '\u26BD'
// → true ✅

// 複数のコードポイントからなる絵文字をマッチさせる
re.test('👨🏾‍⚕️'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'
// → false ❌
```
上の例では、👨🏾‍⚕️ の絵文字は複数のコードポイントから構成されている
EmojiはUnicode文字Propertyであるため、正規表現はマッチしない
[Emoji](https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=[:Emoji=Yes:])


以下のようにvフラグを付けると、Unicode文字列Propertyを利用して、1文字より長い文字列にマッチすることができる
```js
const re = /^\p{RGI_Emoji}$/v;

// 1つのコードポイントからなる絵文字をマッチさせる
re.test('⚽'); // '\u26BD'
// → true ✅

// 複数のコードポイントからなる絵文字をマッチさせる
re.test('👨🏾‍⚕️'); // '\u{1F468}\u{1F3FE}\u200D\u2695\uFE0F'
// → true ✅
```

vフラグは、以下のUnicode文字列プロパティのサポートを最初から有効にしている
- Basic_Emoji
- Emoji_Keycap_Sequence
- RGI_Emoji_Modifier_Sequence
- RGI_Emoji_Flag_Sequence
- RGI_Emoji_Tag_Sequence
- RGI_Emoji_ZWJ_Sequence
- RGI_Emoji

# 集合表記とUnicode文字プロパティ,Unicode文字列プロパティ
- 差集合 --
    Unicode文字プロパティ
    ```js
    /\p{Script_Extensions=Greek}/v.test('π');
    // → true
    /[\p{Script_Extensions=Greek}--π]/v.test('π');
    // → false
    ```
    Unicode文字列プロパティ

    ```js
    /\p{RGI_Emoji_Tag_Sequence}/v.test('🏴󠁧󠁢󠁳󠁣󠁴󠁿');
    // → true
    /[\p{RGI_Emoji_Tag_Sequence}--\q{🏴󠁧󠁢󠁳󠁣󠁴󠁿}]/v.test('🏴󠁧󠁢󠁳󠁣󠁴󠁿');
    // → false
    ```
- 積集合（共通部分） &&
    ```js
    const re = /[\p{Script_Extensions=Greek}&&\p{Letter}]/v;
    // U+03C0 ギリシャ小文字π
    re.test('π'); // → true
    // U+1018A ギリシャで0記号
    re.test('𐆊'); // → false
    ```

- 和集合（合併）
    ```js
    const re = /^[\p{Emoji_Keycap_Sequence}\p{ASCII}\q{🇧🇪|abc}xyz0-9]$/v;
    re.test('4️⃣'); // → true Emoji_Keycap_Sequence
    re.test('_'); // → true ASCII
    re.test('🇧🇪'); // → true
    re.test('abc'); // → true
    re.test('x'); // → true
    re.test('4'); // → true
    ```
# 大文字小文字を区別しないマッチングの改善
iフラグを使ってるので本来なら^\P,\pも同じ結果になるはずが以下のようになる
Lowercase_Letterは小文字のUnicode文字プロパティ
```js
const re1 = /\p{Lowercase_Letter}/giu;
const re2 = /[^\P{Lowercase_Letter}]/giu;

const string = 'aAbBcC4#';

string.replaceAll(re1, 'X');
// → 'XXXXXX4#'

string.replaceAll(re2, 'X');
// → 'aAbBcC4#''
```

vフラグに変えると改善されて、同じ結果になる

```js
const re2 = /[^\P{Lowercase_Letter}]/giv;

const string = 'aAbBcC4#';
string.replaceAll(re2, 'X');
// → 'XXXXXX4#'
```


# 参考
- [RegExp v flag with set notation and properties of strings](https://v8.dev/features/regexp-v-flag#unicode-properties-of-strings)
- [JavaScript 正規表現のフラグ](https://javascript.keicode.com/lang/regex-pattern-flags.php#5)

# その他
[tc39](https://tc39.es/ja/)
>Ecma InternationalのTC39は、JavaScript開発者、実装者、専門家などのグループで、JavaScriptの仕様をメンテナンスし発展させるためにコミュニティと協力しています

# 宣伝させてください！
https://recruit.iimon.co.jp/