1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JS】RegExp の複数行記述、RegExp 内での変数参照がしたいので、RegExp を結合する関数を作った

Last updated at Posted at 2020-09-17

問題

前回の記事 1 では,コードを 80 文字/行以内に収めたいけれど,正規表現リテラルは途中で改行できないので,長い正規表現を一旦文字列型変数に代入してから + で結合 → 正規表現化という手順を踏んでいました.

しかし,この方法には以下のような問題があります.

  • “\” を文字列に含めるには \\ としてやる必要があり,エスケープが二重になって鬱陶しい.
  • 文字列型なので当然正規表現としてのシンタックス・ハイライトはつかない.

解決法

そこで,*/foo/ + /bar|z/ といったかんじで正規表現同士を結合できないか調べてみたところ,RegExp.prototype.source を使う方法を見つけた2ので,それをもとに以下の関数を作ってみました.

TypeScript

concat-regexps.ts
/**
 * @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

concat-regexps.js
/**
 * @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-regex3babel-plugin-transform-modern-regexp を入れても,名前付きキャプチャー・グループと RegExp.prototype.source を併用すると変換がおかしくなるので,第一引数の要素には名前付きキャプチャー・グループを含まないようにする必要があります.4

  1. 本記事の内容を反映して更新したので,編集前のコードは、こちら を見てください.

  2. Cf. How can I concatenate regex literals in JavaScript? - Stack Overflow

  3. Cf. `@babel/plugin-transform-named-capturing-groups-regex` fails to wrap all groups · Issue #10045 · babel/babel

  4. 2020 年 9 月 17 日現在の最新版は,Babel 7.11.0, babel-plugin-transform-modern-regexp 7.10.4, babel-plugin-transform-modern-regexp 0.0.6

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?