Regular Expression(正規表現) / RegExpとは?
RegExpは正規表現のためのJavaScriptの組み込みクラス。
リテラルでの表記とコンストラクタを利用した表記の2通りがある。
TypeScriptにはRegExp型があり、これはJavaScriptのRegExpオブジェクトを表す。
リテラル記法
/ で検索したい文字を囲み、最後にフラグを書く
const re = / ab+c / i ;
const re = /0(8|9)0-[0-9]{4}-[0-9]{4}/g;
-
g(グローバルフラグ)
:文字列内のすべての一致を検索。最初の一致を見つけた後も、次の一致を検索し続ける -
|(パイプ)
:「または」を意味し、括弧( )でグールプ化する事で、意図しないマッチを防ぐ -
[0-9]
:複数の文字のいずれか一つにマッチし、ハイフン( - )を使って範囲指定を使った記述を行える -
{ }
:数字の桁数を指定できる。{4}で4桁を表し、[0-9]{4}とする事で4桁の数字
const re = /a/g;
const str = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
const re: RegExp = /a/g; // RegExp型を指定
const str: string = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
コンストラクタ記法
RegExpコンストラクタに、
第1引数 → 検索したい文字、第2引数 → フラグ
- 最初の引数に文字列のパターンを渡す場合
const re = new RegExp('ab+c', 'i');
または、
- 最初の引数に正規表現リテラルを渡す場合
const re = new RegExp(/ab+c/, 'i');
const re = new RegExp('a', 'g'); // 第1引数 → 検索したい文字、第2引数 → フラグ
const str = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
const re: RegExp = new RegExp('a', 'g'); // RegExp型を指定
const str: string = "banana";
console.log(str.match(re)); // ["a", "a", "a"]
-
matchメソッド
:
→ gフラグがある場合: 正規表現は文字列全体に対してマッチを繰り返し検索し、すべての一致を配列として返す
→ gフラグがない場合、最初に一致した部分を含む配列を返す(マッチが1回のみで ["a"]になる)
→ 一致しない場合、null を返す
コンストラクタ記法ではバックスラッシュの扱いに注意する
const regexp_re = /0(8|9)0-\d{4}-\d{4}/g;
\
については、コンストラクタを利用する場合はふたつ書く必要がある
const regexp_con = new RegExp("0(8|9)0-\\d{4}-\\d{4}", "g");
JavaScriptでは文字列内でバックスラッシュ \
はエスケープ文字として使われる。
つまり、\\
と書くことで、1つのバックスラッシュ を表現する。
\
を文字列として検索したい場合「コンストラクタでは \\\\
と4文字書く必要がある
// バックスラッシュを検索したい場合
const regexp_re = /\\/; // 1つのバックスラッシュを検索するために正規表現で
console.log(regexp_re.test("\\hoge")); // true
// バックスラッシュを検索したい場合
const regexp_con = new RegExp("\\\\"); // 文字列リテラル内で '\\' を表現
console.log(regexp_con.test("\\hoge")); // true
- new RegExp("
\\\\
") では、最初の\\
は JavaScriptの文字列リテラル内でバックスラッシュを表すために2つ必要。(文字列リテラル内でバックスラッシュをエスケープするため) - その後、正規表現内でバックスラッシュを表すためにさらにエスケープする必要があるため、合計で4つのバックスラッシュ(
\\\\
)が必要 -
testメソッド
: 正規表現が文字列の中に1回でもマッチする場合(部分一致)にtrue を返す
どっちを使う?
-
コンストラクタ記法
正規表現のパターンやフラグが 動的に決まる場合、ユーザー入力をもとに正規表現を生成する場合、といった動的に検索する対象を切り替えたい場合 -
リテラル記法
正規表現のパターンが事前に決まっている様な場合、特に理由がない場合
フラグ
g: global
検索はすべての一致を探す。ただし、gの指定がない場合は、最初の1つのみを探す。
const text = "123 abc 456 789";
const regex = /\d+/g;
const matches = text.match(regex);
console.log(matches); // ["123", "456", "789"]
-
\d
: 半角の数字 (digit) -
+
: 1 回以上の繰り返し -
matchメソッド
: 正規表現に一致したすべての部分を配列で返し、なければ null を返す
i: ignoreCase
正規表現を検索する際、大文字小文字を区別ぜず検索を行う。
const msg = "Hello, World!";
const pattern = /WORLD/i; // i フラグは大小区別しない
const isMatch = pattern.test(msg);
console.log(isMatch); // true
-
testメソッド
: 正規表現が文字列の中に1回でもマッチする場合(部分一致)にtrue を返す
m: multiline
対象の文字列が複数行の場合、特定の文字列パターンの検索を行う。
行の開始 (^) や行の終了 ($) が各行の先頭や末尾にマッチするようになる。
const text = `Hello
World
Goodbye
World`;
const mLineStart = /^Hello/m; // ^ 行の開始 各行の先頭にマッチ
const mStart = text.match(mLineStart);
console.log(mStart); // ['Hello']
const mLineEnd = /World$/m; // $ 行の終了 各行の末尾にマッチ
const mEnd = text.match(mLineEnd);
console.log(mEnd); // 2行目と4行目がWorldで終わっているため、["World", "World"]
-
matchメソッド
: 正規表現に一致したすべての部分を配列で返し、なければ null を返す
s: dotAll
正規表現のドット(.)が改行文字(\n)にもマッチするようにするフラグ。
→ 正規表現におけるドット (.) は、通常、任意の1文字を表すが、改行文字 (\n) にはマッチしないため
const sentence = "father I will always be with you";
const regex = /father.*/you/;
console.log(regex.test(sentence)); // true
-
.
: 任意の1文字 -
*
: 直前の要素が0回以上繰り返される(直前の文字がない or 直前の文字が1個以上連続する) -
.*
: 任意の文字が0回以上繰り返される(何でもいい文字の連続) -
testメソッド
: 正規表現が文字列の中に1回でもマッチする場合(部分一致)にtrue を返す
const sentence = "I am your father.\nI will always be with you."; // 改行文字を含む
const regex = /father\..*you\./; // sフラグ未使用
console.log(regex.test(sentence)); // false
-
\.
: リテラルのドット(.)にマッチ
.
: 正規表現におけるドット (.) は、改行文字(\n)にはマッチしない
(.*
は改行を含まない任意の文字列にマッチするため、改行があると一致しない)
const sentence = "I am your father.\nI will always be with you."; // 改行文字を含む
const regex = /father\..*you\./s; // sフラグ使用
console.log(regex.test(sentence)); // true
s フラグを使用すると、改行文字(\n)が含まれていても、正規表現のドット (.) でマッチするようになる
u: unicode
マルチバイトの Unicode 文字を正しく扱うことができる
const regex = /\u{61}/u; // `u` フラグ付き
console.log(regex.test("a")); // true("a" は Unicode コードポイント 0x61 に一致する)
console.log(regex.test("b")); // false("b" は一致しない)
// `u`フラグなし
const regex = /\u{61}/; // "u" が 61 回連続する文字列
console.log(regex.test("u".repeat(61))); // true("u" を 61 回繰り返した文字列は一致する)
-
\u{xxxx} は Unicodeコードポイント を指定する方法で、{} の中にコードポイントを16進数で書く(xxxxの部分)
-
/\u{61}/u
: Unicodeコードポイント U+0061 を指し、"a" に一致 -
\u
: 単なる文字 u として扱われる(\u{61} は u{61} と同じ解釈) -
{num}
: 直前のパターンのnum回繰り返し
例1) \u0041
→ Unicodeコードポイント U+0041 を示し、「A」を表す。
{} を使わない形式もあり、4桁のコードポイントでよく見られる。
例2)
const regex = /^u{61}$/; // 文字「u」を61回繰り返す文字列に一致
console.log(regex.test("u".repeat(61))); // true
例3)
const regex = /u{61}/; // 文字「u」を61回繰り返す文字列に一致
console.log(regex.test("u".repeat(61))); // true
y : sticky
RegExp オブジェクトの lastIndex プロパティが示す位置からのみマッチするかどうかを調べる。
const adress = "東京都中央区銀座";
// yフラグを使用しない場合
const regex = /中央区/;
console.log(regex.test(adress)); // true
// yフラグを使用した場合
const regexY = /中央区/y; // 粘着検索フラグ
//console.log(regexY.lastIndex); // デフォルト 0
console.log(regexY.test(adress)); // false
//console.log(regexY.lastIndex); // 0
console.log(regexY.test(adress)); // false
-
testメソッド
: 正規表現が文字列の中に1回でもマッチする場合(部分一致)にtrue を返す -
lastIndex
: 次に検索を開始すべき位置を示す
yフラグを使用すると、
-
1 回目の検索:lastIndex が示す位置から始まり、最初の検索では、lastIndex は 0 で、文字列の先頭から検索を始める。文字列 "東京都中央区銀座" の最初の部分は "東京都" で、"中央区" は最初には一致しないため、結果、falseを返す
-
2 回目の検索:前回一致しなかったため、lastIndex はリセットされ、そのまま 0 になる。そのため、検索は再び最初の位置(lastIndex = 0)から始まり、再度 "中央区" を見つけられないため、false を返す
lastIndexの位置の変更を加えてみると、以下の様に2回目の検索は trueになる。
const regexY = /中央区/y; // 粘着検索フラグ
//console.log(regexY.lastIndex); // デフォルト 0
console.log(regexY.test(adress)); // false
regexY.lastIndex = 3;
console.log(regexY.test(adress)); // true
参考