アサーション

アサーションには、 行や単語の先頭・末尾を示す境界や、(先読み、後読み、条件式を含む)何らかの方法で照合が可能なことを示す、その他のパターンが含まれます。

試してみましょう

種類

境界型のアサーション

文字 意味
^

入力開始境界アサーション: 入力の先頭に一致します。複数行フラグ (multiline, m) が有効である場合、改行文字の直後にも一致します。例えば /^A/ は "an A" の "A" には一致しませんが、"An A" の先頭の "A" には一致します。

メモ: この文字は、文字クラスパターンの先頭にある場合は異なる意味を持ちます。

$

入力末尾境界アサーション: 入力の末尾に一致します。複数行フラグ (multiline, m) が有効である場合、は、改行文字の直前にも一致します。例えば /t$/ は "eater" の "t" には一致しませんが、"eat" の "t" には一致します。

\b

単語境界アサーション: 単語の境界に一致します。これは、単語構成文字と後に続く非単語構成文字の間、または非単語構成文字と後に続く単語構成文字の間、または文字列の先頭・末尾です。単語の区切りは一致する「文字」ではありません。アンカーのように、単語の区切りは一致した部分に含まれません。言い換えると、一致した単語の区切りの長さは 0 です。

例:

  • /\bm/ は "moon" の "m" に一致します。
  • /oo\b/ は "moon" の "oo" に一致しません。これは "oo" の 後に単語構成文字である "n" が続いているためです。
  • /oon\b/ は "moon" の "oon" に一致します。これは、文字列の終端であって、後に単語構成文字が続いていないためです。
  • /\w\b\w/ はどこにも一致しないでしょう。単語構成文字の後に非単語構成文字と単語構成文字の両方が続くことはありえないからです。

バックスペース文字 ([\b]) については文字クラスを見てください。

\B

非単語境界アサーション: 単語の区切り以外に一致します。これは、前の文字と次の文字が同じ種類である位置のことです。2 つの文字の間や 2 つの空白の間など、どちらも単語構成文字か、あるいはどちらも非単語構成文字でなければなりません。文字列の先頭と末尾は、非単語構成文字とみなされます。一致した単語の区切りと同じように、一致した非単語の区切りも一致に含まれません。例えば、 /\Bon/ は "at noon" の "on" に、 /ye\B/ は "possibly yesterday" の "ye" に一致します。

その他のアサーション

メモ: ? の文字は数量子としても使用されます。

文字 意味
x(?=y)

先読みアサーション: "x" の後に "y" が続く場合のみ "x" に一致します。例えば、 /Jack(?=Sprat)/ は "Jack" の後に "Sprat" が続く場合のみ一致します。
/Jack(?=Sprat|Frost)/ は "Jack" の後に "Sprat" または "Frost" が続く場合のみ一致します。しかし、 "Sprat" も "Frost" も一致した結果には含まれません。

x(?!y)

否定先読みアサーション: "x" の後に "y" が続いていない場合のみ "x" に一致します。例えば、 /\d+(?!\.)/ は数字の後に小数点が続かない場合のみ一致します。/\d+(?!\.)/.exec('3.141')は "141" には一致しますが、 "3" には一致しません。

(?<=y)x

後読みアサーション: "x" の前に "y" がある場合のみ "x" に一致します。例えば、/(?<=Jack)Sprat/ は、 "Jack" が先行する場合のみ、 "Sprat" に一致します。 /(?<=Jack|Tom)Sprat/ は、 "Jack" か "Tom" が先行する場合のみ、 "Sprat" に一致します。しかし、 "Jack" も "Tom" も一致した結果には含まれません。

(?<!y)x

否定後読みアサーション: "x" の前に "y" がない場合のみ "x" に一致します。例えば、 /(?<!-)\d+/ は、数字の前にマイナス記号がない場合のみ、数字に一致します。 /(?<!-)\d+/.exec('3') は "3" に一致します。 /(?<!-)\d+/.exec('-3') では数字の前にマイナス記号があるため、一致するものは見つかりません。

基本的な境界型の例

js
// おかしい文字列を修正するために正規表現の境界を利用します。
buggyMultiline = `tey, ihe light-greon apple
tangs on ihe greon traa`;

// 1) 文字列の最初と改行の直後の一致を修正するために ^ を利用します。
buggyMultiline = buggyMultiline.replace(/^t/gim, "h");
console.log(1, buggyMultiline); // 'tey' => 'hey' に、 'tangs' => 'hangs' に修正します。 'traa' は対象外です。

// 2) テキストの末尾を修正するために $ を利用します。
buggyMultiline = buggyMultiline.replace(/aa$/gim, "ee.");
console.log(2, buggyMultiline); // 'traa' => 'tree.' に修正します。

// 3) 単語と空白の間の境界の右の文字に一致させるために \b を利用します。
buggyMultiline = buggyMultiline.replace(/\bi/gim, "t");
console.log(3, buggyMultiline); // 'ihe' => 'the' に修正しますが、'light'は対象外です。

// 4) エンティティの境界内の文字に一致するために \B を利用します。
fixedMultiline = buggyMultiline.replace(/\Bo/gim, "e");
console.log(4, fixedMultiline); // 'greon' => 'green' に修正しますが、'on' は対象外です。

^ 制御文字を利用した入力の先頭との照合

^ は、通常、入力の先頭に一致させるために利用します。この例では、/^A/ という正規表現で 'A' で始まるフルーツを取得します。ここでの ^ は、入力の先頭を示すという、たった 1 つの役割を果たしています。適切なフルーツを選択するために filter メソッドとアロー関数を用います。

js
const fruits = ["Apple", "Watermelon", "Orange", "Avocado", "Strawberry"];

// /^A/ 正規表現で 'A' で始まるフルーツを選択します。
// ここでの'^' 制御記号は「入力の先頭に一致する」という 1 つの役割だけで利用されています。

const fruitsStartsWithA = fruits.filter((fruit) => /^A/.test(fruit));
console.log(fruitsStartsWithA); // [ 'Apple', 'Avocado' ]

2 番目の例での ^ は、入力の先頭への一致と、文字クラスで用いられた場合の文字集合の否定または補集合という、両方で利用されています。

js
const fruits = ["Apple", "Watermelon", "Orange", "Avocado", "Strawberry"];

// /^[^A]/ という正規表現で 'A' で始まらないフルーツを選択します。
// この例では、'^' 制御記号は 2 つの意味を表しています。
// 1) 入力の先頭に一致
// 2) [^A] という文字集合の否定または補集合
// つまり、角括弧で囲まれた中にないあらゆるものに一致します。

const fruitsStartsWithNotA = fruits.filter((fruit) => /^[^A]/.test(fruit));

console.log(fruitsStartsWithNotA); // [ 'Watermelon', 'Orange', 'Strawberry' ]

それ以外の例は 入力境界アサーションのリファレンスを参照してください。

単語の境界に一致

この例では、 "en" または "ed" で終わる単語を含む果物の名前にを照合します。

js
const fruitsWithDescription = ["Red apple", "Orange orange", "Green Avocado"];

// 単語の終わりに 'en' または 'ed' を含む記述を選択します。
const enEdSelection = fruitsWithDescription.filter((descr) =>
  /(en|ed)\b/.test(descr),
);

console.log(enEdSelection); // [ 'Red apple', 'Green Avocado' ]

それ以外の例は 単語境界アサーションのリファレンスを参照してください。

先読みアサーション

この例は、 "First" という単語の後に "test" という単語が続く場合のみ一致しますが、"test" は照合結果に含まれません。

js
const regex = /First(?= test)/g;

console.log("First test".match(regex)); // [ 'First' ]
console.log("First peach".match(regex)); // null
console.log("This is a First test in a year.".match(regex)); // [ 'First' ]
console.log("This is a First peach in a month.".match(regex)); // null

それ以外の例は 先読みアサーションのリファレンスを参照してください。

基本的な否定先読みアサーション

例えば、/\d+(?!\.)/ は小数点が後に続かない数値にだけ一致します。/\d+(?!\.)/.exec('3.141') は "141" に一致しますが、 "3" には一致しません。

js
console.log(/\d+(?!\.)/g.exec("3.141")); // [ '141', index: 2, input: '3.141' ]

それ以外の例は 先読みアサーションのリファレンスを参照してください。

アサーションと範囲における '?!' の組み合わせの異なる意味での利用

?! の組み合わせを利用するとき、アサーション(/x(?!y)/ など)と文字クラス[^?!] など)では異なる意味を持ちます。

js
const orangeNotLemon =
  "Do you want to have an orange? Yes, I do not want to have a lemon!";

// アサーション /x(?!y)/ と範囲 /[^?!]/ では '?!' の組み合わせの利用は異なる意味を持ちます。
const selectNotLemonRegex = /[^?!]+have(?! a lemon)[^?!]+[?!]/gi;
console.log(orangeNotLemon.match(selectNotLemonRegex)); // [ 'Do you want to have an orange?' ]

const selectNotOrangeRegex = /[^?!]+have(?! an orange)[^?!]+[?!]/gi;
console.log(orangeNotLemon.match(selectNotOrangeRegex)); // [ ' Yes, I do not want to have a lemon!' ]

後読みアサーション

この例では、 "orange" という単語の前に "ripe" という単語がある場合のみ、 "apple" に置き換えます。

js
const oranges = ["ripe orange A", "green orange B", "ripe orange C"];

const newFruits = oranges.map((fruit) =>
  fruit.replace(/(?<=ripe )orange/, "apple"),
);
console.log(newFruits); // ['ripe apple A', 'green orange B', 'ripe apple C']

それ以外の例は 後読みアサーションのリファレンスを参照してください。

関連情報