JavaScriptの正規表現とは

正規表現を使う事でできること


正規表現を使う事で、テキストデータのパターンマッチングや検索、置換、バリデーションなど、さまざまなことができます。以下に正規表現の主な用途をいくつか示します:

  1. パターンマッチング: 正規表現は特定のパターンに一致するテキストを検索するために使用されます。例えば、メールアドレス、電話番号、URLなどの特定の形式に一致するデータを検索することができます。
  2. テキストの抽出: 正規表現を使用して、テキストデータから特定の情報を抽出することができます。例えば、HTMLやXMLからタグ内のテキスト、ログファイルから特定のイベントの情報などを抽出することができます。
  3. テキストの置換: 正規表現はテキスト内の特定のパターンを他の文字列で置換するために使用されます。例えば、特定の単語を他の単語に置き換えたり、文字列内の不要なスペースを削除するなどの操作が可能です。
  4. バリデーション: 正規表現は入力データのバリデーションに使用されます。特定の形式やパターンに一致しないデータを検出したり、不正なデータの送信を防ぐために使用することができます。例えば、パスワードの要件(大文字、小文字、数字を含むなど)をチェックする場合に正規表現を利用できます。
  5. 文字列の分割: 正規表現を使用して、テキストを特定のパターンで分割することができます。例えば、カンマで区切られたデータを分割して配列に格納するなどが可能です。
  6. フィルタリング: 正規表現を使用して、テキストデータから特定の条件に一致する行や要素をフィルタリングすることができます。例えば、ログファイルから特定のエラーメッセージを含む行を抽出するなどができます。

正規表現は非常に柔軟で強力なツールであり、テキスト処理やパターンマッチングにおいて幅広く活用されています。しかし、正規表現の使用は適切な文脈と適切な知識に基づいて行われる必要があります。

正規表現とは

正規表現(正規表現、regexとも呼ばれる)は、開発者が文字列をパターンに一致させたり、サブマッチの情報を抽出したり、単純に文字列がそのパターンに適合するかどうかをテストしたりするためのものです。正規表現は多くのプログラミング言語で使用されており、JavaScriptの構文はPerlに触発されています。

概要

正規表現は形式言語理論における重要な概念です。正規表現は、可能性のある無限の文字列の集合(言語と呼ばれる)を記述する方法です。正規表現は、次の機能を必要とします。

  • アルファベットとして使用できる文字の集合(アルファベット)。
  • 連結:ab は「文字 a の後に文字 b が続く」という意味です。
  • 和集合:a|b は「a または b のいずれか」という意味です。
  • クリーンスター:a* は「0個以上の a 文字」という意味です。

有限のアルファベット(英語のアルファベットの26文字や、Unicode文字セット全体など)を想定すると、上記の機能ですべての正規言語を生成することができます。もちろん、「10桁の数字」や「スペースではない文字」といったパターンは、非常に煩雑になることが多いため、JavaScriptの正規表現には、以下で紹介する多くの省略記法が含まれています。

注:JavaScriptの正規表現は実際には正規ではありません。バックリファレンスの存在により(正規表現は有限の状態を持つ必要があります)、正規ではありません。ただし、それらは非常に便利な機能です。

正規表現の作成 正規表現は通常、パターンをスラッシュ(/)で囲むことによってリテラルとして作成されます。

const regex1 = /ab+c/g;


正規表現はRegExp()コンストラクタを使用しても作成できます。

const regex2 = new RegExp("ab+c", "g");


これらにはランタイム上の違いはありませんが、パフォーマンス、静的解析可能性、およびエスケープ文字の作成のしやすさに関連する問題に影響を与える場合があります。

正規表現フラグ

フラグは、正規表現の解釈方法や入力テキストとの相互作用方法を変更できる特別なパラメータです。各フラグは、RegExpオブジェクト上の1つのアクセサプロパティに対応します。

以下のセクションでは、構文の性質に基づいてグループ化された、利用可能な正規表現構文のすべてをリストしています。

フラグ説明プロパティ
d部分一致のためのインデックスを生成します。hasIndices
gグローバル検索を実行します。global
i大文字と小文字を区別しない検索を行います。ignoreCase
m^および$が改行文字に一致するようにします。multiline
s.が改行文字に一致するようにします。dotAll
u“Unicode”。パターンをUnicodeコードポイントのシーケンスとして扱います。unicode
y“sticky”検索を実行し、対象の文字列の現在の位置から一致を開始します。sticky

フラグを使用した正規表現のサンプルコード:

// フラグ: d
const regex1 = /\w+/d;
const text1 = "Hello, World!";
console.log(text1.match(regex1)); // ["Hello"]

// フラグ: g
const regex2 = /o/g;
const text2 = "Hello, World!";
console.log(text2.match(regex2)); // ["o", "o"]

// フラグ: i
const regex3 = /hello/i;
const text3 = "Hello, World!";
console.log(regex3.test(text3)); // true

// フラグ: m
const regex4 = /^h/m;
const text4 = "Hello\nWorld!";
console.log(regex4.test(text4)); // false

// フラグ: s
const regex5 = /l.s/s;
const text5 = "Hello\nWorld!";
console.log(text5.match(regex5)); // ["l\ns"]

// フラグ: u
const regex6 = /\u{1F600}/u;
const text6 = "Hello, 😀 World!";
console.log(regex6.test(text6)); // true

// フラグ: y
const regex7 = /wo/y;
const text7 = "Hello, World!";
console.log(regex7.exec(text7)); // null
console.log(regex7.exec(text7)); // ["wo"]

上記のコードでは、それぞれのフラグに基づいて正規表現パターンを評価しています。matchメソッドやtestメソッド、execメソッドを使用して、対象の文字列においてパターンに一致する部分を取得したり、一致するかどうかをテストしたりしています。

アサーション

アサーションは、指定した位置で文字列が特定の条件を満たすかどうかをテストする構造ですが、文字を消費しません。アサーションは数量化できません。

入力境界アサーション:^、$ 現在の位置が入力の先頭または末尾であること、またはmフラグが設定されている場合は行の先頭または末尾であることをアサートします。

先読みアサーション:(?=…)、(?!…) 現在の位置の後に特定のパターンが続くかどうかをアサートします。

後読みアサーション:(?<=…)、(?<!…) 現在の位置が特定のパターンに先行しているかどうかをアサートします。

単語境界アサーション:\b、\B 現在の位置が単語境界であることをアサートします。

各アサーションを使用した正規表現のサンプルコード:

// 入力境界アサーション: ^
const regex1 = /^hello/;
const text1 = "hello, world!";
console.log(regex1.test(text1)); // true

// 入力境界アサーション: $
const regex2 = /world!$/;
const text2 = "hello, world!";
console.log(regex2.test(text2)); // true

// 入力境界アサーション: ^ および $
const regex3 = /^hello$/;
const text3 = "hello";
console.log(regex3.test(text3)); // true

// 入力境界アサーション: m
const regex4 = /^hello$/m;
const text4 = "hello\nworld!";
console.log(regex4.test(text4)); // false

// 先読みアサーション: (?=...)
const regex5 = /hello(?=,)/;
const text5 = "hello, world!";
console.log(regex5.test(text5)); // true

// 先読みアサーション: (?!...)
const regex6 = /hello(?! world)/;
const text6 = "hello, world!";
console.log(regex6.test(text6)); // false

// 後読みアサーション: (?<=...)
const regex7 = /(?<=hello,) world/;
const text7 = "hello, world!";
console.log(regex7.test(text7)); // true

// 後読みアサーション: (?<!...)
const regex8 = /(?<!hello,) world/;
const text8 = "hello, world!";
console.log(regex8.test(text8)); // false

// 単語境界アサーション: \b
const regex9 = /\bhello\b/;
const text9 = "hello, world!";
console.log(regex9.test(text9)); // true

// 単語境界アサーション: \B
const regex10 = /\Bello\B/;
const text10 = "hello, world!";
console.log(regex10.test(text10)); // true

上記のコードでは、各アサーションを含む正規表現パターンを使用して、対象の文字列に対してパターンマッチングを行っています。それぞれのアサーションに基づいて、文字列内での位置や条件に一致するかどうかをテストしています。

アトム

アトムは正規表現の最も基本的な単位です。各アトムは、文字列内の1つ以上の文字を消費し、一致に失敗するか次のアトムとの一致を許可します。

バックリファレンス:\1、\2 以前にキャプチャグループでキャプチャされた以前の一致に一致します。

キャプチャグループ:(…) サブパターンに一致し、一致に関する情報を覚えておきます。

文字クラス:[…]、[^…] 文字の集合内のいずれかの文字に一致するか、集合内の文字ではないかに一致します。

文字クラスエスケープ:\d、\D、\w、\W、\s、\S あらかじめ定義された文字の集合内のいずれかの文字に一致するか、集合内の文字ではないかに一致します。

文字エスケープ:\n、\u{…} 文字に一致しますが、そのリテラル形式で便利に表現できない場合があります。

リテラル文字:a、b 特定の文字に一致します。

名前付きバックリファレンス:\k<name> 名前付きキャプチャグループでキャプチャされた以前の一致に一致します。

名前付きキャプチャグループ:(?<name>…) サブパターンに一致し、一致に関する情報を覚えておきます。グループは後でパターン内のインデックスではなく、カスタム名で識別できます。

非キャプチャグループ:(?:…) 一致に関する情報を覚えずにサブパターンに一致します。

Unicode文字クラスエスケープ:\p{…}、\P{…} Unicodeプロパティで識別されるUnicode文字の集合内のいずれかの文字に一致するか、集合内の文字ではないかに一致します。

ワイルドカード:. sフラグが設定されていない限り、行終端以外の任意の文字に一致します。

アトムを使用した正規表現のサンプルコード:

// バックリファレンス: \1
const regex1 = /(cat)\s\1/;
const text1 = "cat cat";
console.log(regex1.test(text1)); // true

// キャプチャグループ: (...)
const regex2 = /(cat)\s(dog)/;
const text2 = "cat dog";
console.log(regex2.test(text2)); // true

// 文字クラス: [...]
const regex3 = /[aeiou]/;
const text3 = "Hello";
console.log(regex3.test(text3)); // true

// 文字クラスエスケープ: \d
const regex4 = /\d/;
const text4 = "123";
console.log(regex4.test(text4)); // true

// 文字エスケープ: \n
const regex5 = /Hello\nWorld/;
const text5 = "Hello\nWorld";
console.log(regex5.test(text5)); // true

// リテラル文字: a
const regex6 = /abc/;
const text6 = "abc";
console.log(regex6.test(text6)); // true

// 名前付きバックリファレンス: \k<name>
const regex7 = /(?<animal>cat)\s\k<animal>/;
const text7 = "cat cat";
console.log(regex7.test(text7)); // true

// 名前付きキャプチャグループ: (?<name>...)
const regex8 = /(?<animal>cat)\s(?<animal>dog)/;
const text8 = "cat dog";
console.log(regex8.test(text8)); // true

// 非キャプチャグループ: (?:...)
const regex9 = /(?:cat)\s(?:dog)/;
const text9 = "cat dog";
console.log(regex9.test(text9)); // true

// Unicode文字クラスエスケープ: \p{...}
const regex10 = /\p{Script=Greek}/u;
const text10 = "αβγ";
console.log(regex10.test(text10)); // true

// ワイルドカード: .
const regex11 = /h.t/;
const text11 = "hat";
console.log(regex11.test(text11)); // true

上記のコードでは、各アトムを使用した正規表現パターンを示しています。それぞれのアトムは特定の文字やパターンに一致するために使用されます。各サンプルコードは、対象の文字列と正規表現パターンのマッチング結果を出力しています。

その他の機能

これらの機能自体はパターンを指定しませんが、パターンを構成するために使用されます。

選択:| |文字で区切られたセットのいずれかに一致します。

数量子:*、+、?、{n}、{n,}、{n,m} アトムを特定の回数一致させます。

選択と数量子を使用した正規表現のサンプルコード:

// 選択: |
const regex1 = /apple|banana|cherry/;
const text1 = "I love banana";
console.log(regex1.test(text1)); // true

// 数量子: *
const regex2 = /go*d/;
const text2 = "gd";
console.log(regex2.test(text2)); // true

// 数量子: +
const regex3 = /go+d/;
const text3 = "good";
console.log(regex3.test(text3)); // true

// 数量子: ?
const regex4 = /colou?r/;
const text4 = "color";
console.log(regex4.test(text4)); // true

// 数量子: {n}
const regex5 = /go{2}d/;
const text5 = "good";
console.log(regex5.test(text5)); // true

// 数量子: {n,}
const regex6 = /go{2,}d/;
const text6 = "gooood";
console.log(regex6.test(text6)); // true

// 数量子: {n,m}
const regex7 = /go{2,4}d/;
const text7 = "gooood";
console.log(regex7.test(text7)); // true

上記のコードでは、選択と数量子を使用して正規表現パターンを作成しています。選択では、| 文字で区切られたセットのいずれかに一致します。数量子では、アトムを特定の回数一致させるために使用されます。各サンプルコードは、対象の文字列と正規表現パターンのマッチング結果を出力しています。

test()メソッド

testは、JavaScriptの正規表現オブジェクトのメソッドの1つです。testメソッドは、指定した文字列が正規表現パターンに一致するかどうかを評価します。

正規表現オブジェクトのtestメソッドを呼び出すと、対象の文字列が正規表現パターンに一致する場合にはtrueを返し、一致しない場合にはfalseを返します。testメソッドは単純な真偽値を返すだけで、一致した具体的な情報を取得することはできません。

正規表現オブジェクト

正規表現オブジェクトには、以下のようなメソッドがあります。

  1. exec(): マッチするテキストを検索し、最初の一致項目の情報を返します。一致が見つからない場合は null を返します。
  2. test(): マッチするテキストが存在するかどうかを判定します。マッチが見つかれば true を返し、見つからなければ false を返します。
  3. match(): テキスト内の正規表現に一致する部分文字列を検索し、結果を配列として返します。
  4. search(): テキスト内で正規表現に一致する位置を検索し、最初の一致のインデックスを返します。見つからない場合は -1 を返します。
  5. replace(): テキスト内の正規表現に一致する部分文字列を置換し、結果を新しい文字列として返します。
  6. split(): テキストを正規表現に基づいて分割し、結果を配列として返します。

これらのメソッドは、正規表現オブジェクトに対して直接呼び出すことができます。例えば、以下のように使用します。

const regex = /pattern/;
const text = "This is a sample text with the pattern.";

const result1 = regex.exec(text);
console.log(result1); // マッチ結果の情報を出力

const result2 = regex.test(text);
console.log(result2); // マッチの有無を判定し、結果を出力

const result3 = text.match(regex);
console.log(result3); // マッチした部分文字列の配列を出力

const result4 = text.search(regex);
console.log(result4); // 最初のマッチのインデックスを出力

const result5 = text.replace(regex, "replacement");
console.log(result5); // マッチした部分文字列を置換して結果を出力

const result6 = text.split(regex);
console.log(result6); // 正規表現に基づいてテキストを分割した結果を出力

これらのメソッドを使用することで、正規表現を活用してテキストの検索、置換、分割などの操作が可能です。必要に応じて適切なメソッドを選択し、正規表現のパターンとテキストを処理してください。