iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

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

背景

私が以前書いた記事でこの正規表現について勉強する為です。

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

u (unicode) フラグ

ES2015 に u (unicode) フラグが導入され、コードポイント単位で正規表現を扱えるようになった。

  • コードポイントとは Code Point

    「文字」に対する「一意のID」のこと

    "A" という文字には、 U+0041 というコードポイントが割り当てられる

      console.log('A'.codePointAt(0).toString(16))
      //41
      console.log(`\\u{41}`)
      //'A'
    

    Code Pointは\u{Code Pointの16進数の値}のようにエスケープシーケンスとして記述できる

    以下の例のように、uフラグをつけることでUnicode文字を正規表現で扱えます。

      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{...}

\p{…},\P{…} を使うには、uフラグをセットで使います。 Unicode文字Propertyにどんなものがわからなかったですが、こちら参考になりました。 Unicode Utilities: Character Properties

// "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

以下のようにvフラグを付けると、Unicode文字列Propertyを利用して、1文字より長い文字列にマッチすることができる

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文字プロパティ

      /\\p{Script_Extensions=Greek}/v.test('π');
      // → true
      /[\\p{Script_Extensions=Greek}--π]/v.test('π');
      // → false
    

    Unicode文字列プロパティ

      /\\p{RGI_Emoji_Tag_Sequence}/v.test('🏴󠁧󠁢󠁳󠁣󠁴󠁿');
      // → true
      /[\\p{RGI_Emoji_Tag_Sequence}--\\q{🏴󠁧󠁢󠁳󠁣󠁴󠁿}]/v.test('🏴󠁧󠁢󠁳󠁣󠁴󠁿');
      // → false
    
  • 積集合(共通部分) &&

      const re = /[\\p{Script_Extensions=Greek}&&\\p{Letter}]/v;
      // U+03C0 ギリシャ小文字π
      re.test('π'); // → true
      // U+1018A ギリシャで0記号
      re.test('𐆊'); // → false
    
  • 和集合(合併)

      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文字プロパティ

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フラグに変えると改善されて、同じ結果になる

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

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

参考

その他

tc39

Ecma InternationalのTC39は、JavaScript開発者、実装者、専門家などのグループで、JavaScriptの仕様をメンテナンスし発展させるためにコミュニティと協力しています