問題
前回の記事 1 では,コードを 80 文字/行以内に収めたいけれど,正規表現リテラルは途中で改行できないので,長い正規表現を一旦文字列型変数に代入してから +
で結合 → 正規表現化という手順を踏んでいました.
しかし,この方法には以下のような問題があります.
- “\” を文字列に含めるには
\\
としてやる必要があり,エスケープが二重になって鬱陶しい. - 文字列型なので当然正規表現としてのシンタックス・ハイライトはつかない.
解決法
そこで,*/foo/ + /bar|z/
といったかんじで正規表現同士を結合できないか調べてみたところ,RegExp.prototype.source
を使う方法を見つけた2ので,それをもとに以下の関数を作ってみました.
TypeScript
/**
* @author JuthaDDA
* @see [RegExp の複数行記述、RegExp 内での変数参照がしたいので、
* 正規表現を結合する関数を作った - Qiita
* ](https://qiita.com/juthaDDA/items/f1093b968faa3d810c1c)
* @param regExps - Babel (< 7.11.0) を使う場合は,
* 名前付きキャプチー・グループを含むと正しく変換されない.
* `@babel/plugin-transform-named-capturing-groups-regex (< 7.10.4)` も未対応.
*/
const concatRegExps = ( regExps:RegExp[], flags?:string ):RegExp => {
return RegExp(
regExps.reduce( ( acc, cur ) => acc + cur.source, '' ),
flags,
);
};
JavaScript
/**
* @author JuthaDDA
* @see [RegExp の複数行記述、RegExp 内での変数参照がしたいので、
* 正規表現を結合する関数を作った - Qiita
* ](https://qiita.com/juthaDDA/items/f1093b968faa3d810c1c)
* @param {RegExp[]} regExps - Babel (< 7.11.0) を使う場合は,
* 名前付きキャプチー・グループを含むと正しく変換されない.
* `@babel/plugin-transform-named-capturing-groups-regex` (< 7.10.4) も未対応.
* @param {string} [flags]
* @return {RegExp}
*/
const concatRegExps = ( regExps, flags ) => {
return RegExp(
regExps.reduce( ( acc, cur ) => acc + cur.source, '' ),
flags,
);
};
説明
concatRegExps()
第 1 引数に RegExp
の配列 regExps
を,第 2 引数に 'g'
などの flag
を取ります.
RegExp.prototype.source
には,“\” などをエスケープして string
化したソース・テキストが入っているので,これを regExps.reduce()
で結合し,flag
で指定したフラグを附けた RegExp
を生成して return
しています.なお,0 === regExps.length
の場合の戻り値は,\(?:)\
です.
regExps
の要素には,当然 RegExp
型の変数を含むことができるので,
const width = '50%';
const realNumRegExp = /[+-]?\d*\.?\d+/;
const unitRegExp = /(?:px|em|rem|vw|%)/;
const widthRegExp = concatRegExps( [ /^/, realNumRegExp, unitRegExp, /$/ ] );
const isValidWidth = widthRegExp.test( width );
のような使い方も可能です.
補足
Babel を使う場合は,@babel/plugin-transform-named-capturing-groups-regex3 や babel-plugin-transform-modern-regexp を入れても,名前付きキャプチャー・グループと RegExp.prototype.source
を併用すると変換がおかしくなるので,第一引数の要素には名前付きキャプチャー・グループを含まないようにする必要があります.4
-
Cf. How can I concatenate regex literals in JavaScript? - Stack Overflow ↩
-
Cf. `@babel/plugin-transform-named-capturing-groups-regex` fails to wrap all groups · Issue #10045 · babel/babel ↩
-
2020 年 9 月 17 日現在の最新版は,Babel 7.11.0, babel-plugin-transform-modern-regexp 7.10.4, babel-plugin-transform-modern-regexp 0.0.6 ↩