コンテンツにスキップ

JavaScript/RegExp

出典: フリー教科書『ウィキブックス(Wikibooks)』
JavaScript > RegExpオブジェクト

RegExp

[編集]

RegExpオブジェクトは正規表現を扱うための標準組み込みオブジェクトである。

正規表現とは、文字列の検索や置換に用いられる文字規則の表現である。 JavaScriptは高度な正規表現の機能を備えている。

正規表現は、Unixのgrepやsedで文字列の検索の機能として採用される。その後awkという言語がそれに続き、Perlがその文法を受けついだ。 正規表現は言語ごとに機能の違いがあり、言語間の移植にあたっては注意を払う必要がある。

次にHTMLの要素名を取得する例を示す。要素名は"<"の文字に続く「先頭が英字でそれに0文字以上の英数字が続く文字列」である。これを正規表現で検索するには、以下のように記述する。

/<[a-zA-Z][a-zA-Z0-9]*/

これでHTMLの要素名を取得するには完全ではないのだが、とりあえずその点を飛ばして解説をする。 JavaScriptでは、正規表現をスラッシュ"/"の間に記述をする。 この規則の場合は小なり記号"<"に続き「 先頭が英字 で それに続き英数字が0文字以上(*)続く」ものがが要素名であるとすることでタグを検索している。

スラッシュで囲う表記法はJavaScriptで用意されている正規表現のリテラル表記で、実際には次の3つは意味的に等価である。

const regexp = /ab/;
const regexp = new RegExp( "ab" );
const regexp = new RegExp( /ab/ );

正規表現の作り方

[編集]

正規表現には、前述した「*」以外にも様々な特殊記号が存在している。 まずはそれらを一覧する。

クエスチョン『?』

[編集]

直前のパターンが存在しなくてもいいような場合を表す。

/html?/

はhtmlとhtmいずれにもマッチする。

プラス『*』

[編集]

直前のパターンを0回以上繰り返した文字列を表す。 "Yaho*"と表記すると、"Yahoo"や"Yahooooooo"、"Yah"にマッチする。

プラス『+』

[編集]

直前のパターンを1回以上繰り返した文字列を返す。 「Go+gle」と表記すると"Google"、"Goooooogle"でもマッチするが"Ggle"ではマッチしない点がアスタリスクでの表記と異なる。

中括弧『{n}』

[編集]

n回繰り返したパターンを表す。 "{n,m}"ではn回以上、m回以下にマッチし"{n,}"でn回以上にマッチする。

例を挙げる。次の例は"Google"や"Goooooogle"にはマッチするが、"Gogle"にはマッチしない。

/Go{2,}gle/;

"*", "+", "?", "{n}", "{n,}", "{n,m}" 等のパターンのマッチ回数を表す文字を量指定子と言う。

ドット『.』

[編集]

改行以外の任意の1文字を表す。

ハット記号『^』

[編集]

文頭を表す。

ドル記号『$』

[編集]

文末を表す。

フェンス『|』

[編集]

指定したもののいずれかを表す。

括弧『(?:)』

[編集]

括弧の中の文字列を表すが、後方参照のグループに含まれない。フェンスと組み合わせて次のように使用できる。

/(?:りんご|ゴリラ|ラッパ)/

括弧『()』

[編集]

括弧の中の文字列を表し、後方参照のグループに含める

ブラケット『[]』

[編集]

囲った範囲の文字のいずれか1文字を表す。 つまり次の2文は同じ意味を持つ。

/(?:0|1|2|3|4|5|6|7|8|9)/;
/[0123456789]/;

ブラケット『[A-Z]』

[編集]

A~Zまでの文字列のいずれかを表す。 同様に[a-z]では小文字のa~zを、[0-9]では数字の0~9のいずれかを表す。 ちなみに、ブラケットとハイフンの組み合わせは複数記述できるため、 [0-9a-zA-Z_]とすれば、数字かアルファベット、アンダーバーのいずれかにマッチする。

否定演算子『^』

[編集]

ブラケットの中で使用し、指定した文字以外を意味する。 [^A-Z]では大文字のアルファベット以外を意味する。

半角スペース『 』

[編集]

1文字分のスペースを表す。

バックスラッシュ『\』

[編集]

直後の1文字の特殊文字をエスケープする。\|とすると"|"という文字を表す

その他の特殊文字

[編集]

JavaScriptでは次のような特殊文字も用意している。 これらはPerlで拡張された正規表現から受け継いでいる。

特殊文字 結果
\t 1文字分のタブを表す。
\f フォームフィールドを表す。
\r キャリッジリターンを表す。
\n 改行記号を表す。
[\b] バックスペースを表す。
\d 1文字分の数字を表す。"[0-9]"に等しい
\D 数字以外の1文字を表す。"[^\d]"に等しい
\w 数字とアルファベットかアンダースコア(_)のいずれかを表す。"[0-9a-zA-Z_] "と等しい。
\W \w以外の1文字を表す。"[^\w] "と等しい。
\s 空白改行を表す。"[\t\n\v\f\r \u00a0\u2000-\u200b\u2028-\u3000]"と等しい。
\S \s以外の1文字を表す。"[^\s]"と等しい。
\b 単語の境界を表す。
\B 単語がそこで終わらないことを意味する。

\b,\B のみ新しい概念なので解説する。

/alpha\b/;

の場合、"alpha beta"にはマッチするが、"alphabet"にはマッチしない。 "\B"はその逆で"alphabet"にはマッチするが、"alpha beta"にはマッチしない。

オプション

[編集]

文字列"abrakadabra"に2箇所存在する"ab"の検索を考える。オプションを指定せずに検索を行うと最初の"ab"にはマッチするが、次の"ab"にマッチをしない。 全てにマッチするには、正規表現のオプションを利用して次のように表記する。

/ab/g

最後についているgがオプションで、globalを表す。実行結果は次のようになる。

const s = "abrakadabra";
s = s.replace( /ab/g, '**' ); // **rakad**ra

その他にも、さまざまなオプションが存在する。組み合わせて利用する場合には連続して記述をすればよい。

/ab/g;
/ab/igm;  // i,g,mの3オプション
new RegExp( "ab", "i" );
new RegExp( "ab", "gm" );

それぞれのオプションの意味は次の通りである。

オプション 説明
g 全文検索を行う。
i 大文字、小文字の区別無くマッチする。
m 行頭、行末のマッチに対応する。

最長マッチ

[編集]

基本的に、正規表現はマッチする限り、一番長い組み合わせを取得しようとする。これを最長マッチ(貪欲なマッチ)と言う。 これは「\w+」としてマッチする限り文字を拾い続ければ単語が取得できる事からも自然な仕様と言える。しかし、「<.+>」の様に記述した場合には、HTMLのタグ一つではなく、文中の一番長い"<"と">"の間を取得する。 最も短い組み合わせを取得するには、量指定子(quantifier)に続いて"?"を付ける。これを最短マッチと言う。

後方参照

[編集]

正規表現中で括弧「()」を使用して囲った文字列は、後方参照の対象となる。 マッチした文字列は保存され、後に取得が可能である。

// HTML中のタグを取得する
const s="bbb<em>aaaa</em>ccc";
const m = s.match(/<.+?>(.*?)<\/.+?>/);
m[0];	// <em>aaaa</em> が返る。
m[1];	// aaaa が返る。

1、2、3とマッチした文字列が括弧の順に格納されている。 インデックスの0はマッチした文字列全体が格納されている。

後方参照の必要がない場合には、括弧の内部に"?:"を挿入して次の様に記述する。

/(?:abc)+/;

$1~$9

[編集]

正規表現を実行して、マッチした結果を返す。

// i,b,big等のタグをemタグへ変換する
s.replace( /(<\/?)(?:i|b|s|small|strike|big|tt)/i, '$1em' ); // $1で参照結果("<"か"</")を呼び出し

先読み

[編集]

Disjunction一致部分は結果に含まれない。

パターン 説明
(?=Disjunction) 先読み言明(Lookahead assertions)。パターンDisjunctionが後に続くことが必要。
(?!Disjunction) 否定先読み言明。パターンDisjunctionが後に続かないことが必要。

静的プロパティ

[編集]

RegExp.$&

[編集]

最後に一致した文字列を返すプロパティ。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.$&); // "is"

RegExp.$'

[編集]

最後に一致した文字列の後にある文字列を返すプロパティ。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.$'); // " awesome!"

RegExp.$+

[編集]

最後のキャプチャグループに一致する部分を返すプロパティ。

const str = "123-456-7890";
const regex = /(\d+)-(\d+)-(\d+)/;
regex.test(str);
console.log(RegExp.$+); // "7890"

RegExp.$1

[編集]

最初のキャプチャグループに一致する部分を返すプロパティ。

const str = "123-456-7890";
const regex = /(\d+)-(\d+)-(\d+)/;
regex.test(str);
console.log(RegExp.$1); // "123"

RegExp.$2

[編集]

2番目のキャプチャグループに一致する部分を返すプロパティ。

const str = "123-456-7890";
const regex = /(\d+)-(\d+)-(\d+)/;
regex.test(str);
console.log(RegExp.$2); // "456"

RegExp.$3

[編集]

3番目のキャプチャグループに一致する部分を返すプロパティ。

const str = "123-456-7890";
const regex = /(\d+)-(\d+)-(\d+)/;
regex.test(str);
console.log(RegExp.$3); // "7890"

RegExp.$4

[編集]

4番目のキャプチャグループに一致した文字列を返します。

const str = "Name: John, Age: 30, City: Tokyo";
const regex = /Name: (\w+), Age: (\d+), City: (\w+)/;
regex.test(str);
console.log(RegExp.$4); // undefined(4番目のキャプチャグループがないため)

RegExp.$5

[編集]

5番目のキャプチャグループに一致した文字列を返します。

const str = "Group1: A, Group2: B, Group3: C";
const regex = /Group1: (\w), Group2: (\w), Group3: (\w)/;
regex.test(str);
console.log(RegExp.$5); // undefined(5番目のキャプチャグループがないため)

RegExp.$6

[編集]

6番目のキャプチャグループに一致した文字列を返します。

const regex = /(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)/;
const str = "a-b-c-d-e-f";
regex.test(str);
console.log(RegExp.$6); // "f"

RegExp.$7

[編集]

7番目のキャプチャグループに一致した文字列を返します。

const regex = /(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)/;
const str = "a-b-c-d-e-f-g";
regex.test(str);
console.log(RegExp.$7); // "g"

RegExp.$8

[編集]

8番目のキャプチャグループに一致した文字列を返します。

const regex = /(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)/;
const str = "a-b-c-d-e-f-g-h";
regex.test(str);
console.log(RegExp.$8); // "h"

RegExp.$9

[編集]

9番目のキャプチャグループに一致した文字列を返します。

const regex = /(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)-(\w+)/;
const str = "a-b-c-d-e-f-g-h-i";
regex.test(str);
console.log(RegExp.$9); // "i"

RegExp.$_

[編集]

最後に検索された文字列を返すプロパティ。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.$_); // "JavaScript is awesome!"

RegExp.$`

[編集]

最後に一致した文字列の前にある文字列を返すプロパティ。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.$`); // "JavaScript "

RegExp.input

[編集]

最後に検索された文字列を返します。これは RegExp.$_ と同じです。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.input); // "JavaScript is awesome!"

RegExp.lastMatch

[編集]

最後に一致した文字列を返します。これは RegExp.$& と同じです。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.lastMatch); // "is"

RegExp.lastParen

[編集]

最後のキャプチャグループに一致した文字列を返します。これは RegExp.$+ と同じです。

const str = "123-456-7890";
const regex = /(\d+)-(\d+)-(\d+)/;
regex.test(str);
console.log(RegExp.lastParen); // "7890"

RegExp.leftContext

[編集]

最後に一致した文字列の前にある部分文字列を返します。これは RegExp.$` と同じです。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.leftContext); // "JavaScript "

RegExp.rightContext

[編集]

最後に一致した文字列の後にある部分文字列を返します。これは RegExp.$' と同じです。

const str = "JavaScript is awesome!";
const regex = /is/;
regex.test(str);
console.log(RegExp.rightContext); // " awesome!"

RegExp.prototype

[編集]

すべての正規表現インスタンスが継承するプロトタイプオブジェクトです。

console.log(RegExp.prototype.test); // [Function: test]

RegExp.name

[編集]

RegExp クラスの名前を表すプロパティ(通常は "RegExp" を返します)。

console.log(RegExp.name); // "RegExp"

RegExp.length

[編集]

RegExp コンストラクタの引数の数を表すプロパティ(通常は 2 を返します)。

console.log(RegExp.length); // 2

インスタンスプロパティ

[編集]

RegExp.prototype.dotAll

[編集]

s フラグが設定されている場合に true を返します。

const regex = /foo.bar/s;
console.log(regex.dotAll); // true

RegExp.prototype.flags

[編集]

正規表現のフラグを文字列として返します。

const regex = /test/gi;
console.log(regex.flags); // "gi"

RegExp.prototype.global

[編集]

g フラグが設定されている場合に true を返します。

const regex = /test/g;
console.log(regex.global); // true

RegExp.prototype.hasIndices

[編集]

d フラグが設定されている場合に true を返します。

const regex = /test/d;
console.log(regex.hasIndices); // true

RegExp.prototype.ignoreCase

[編集]

i フラグが設定されている場合に true を返します。

const regex = /test/i;
console.log(regex.ignoreCase); // true

RegExp.prototype.multiline

[編集]

m フラグが設定されている場合に true を返します。

const regex = /test/m;
console.log(regex.multiline); // true

RegExp.prototype.source

[編集]

正規表現のパターン文字列を返します。

const regex = /test/g;
console.log(regex.source); // "test"

RegExp.prototype.sticky

[編集]

y フラグが設定されている場合に true を返します。

const regex = /test/y;
console.log(regex.sticky); // true

RegExp.prototype.unicode

[編集]

u フラグが設定されている場合に true を返します。

const regex = /\u{1F600}/u;
console.log(regex.unicode); // true

RegExp.prototype.unicodeSets

[編集]

v フラグ(Unicode セット)が設定されている場合に true を返します。

const regex = /\p{Letter}/v;
console.log(regex.unicodeSets); // true

RegExp.prototype.exec()

[編集]

文字列内で正規表現に一致する部分を検索し、詳細情報を含む配列を返します。一致しない場合は null を返します。

const regex = /(\d+)-(\d+)/;
const str = "Phone: 123-456";
const result = regex.exec(str);
console.log(result);
// ["123-456", "123", "456"]
// result[1] は "123", result[2] は "456"

RegExp.prototype.test()

[編集]

正規表現が文字列に一致するかをテストし、true または false を返します。

const regex = /hello/;
console.log(regex.test("hello world")); // true
console.log(regex.test("world")); // false

RegExp.prototype.toString()

[編集]

正規表現オブジェクトを文字列として返します。

const regex = /test/gi;
console.log(regex.toString()); // "/test/gi"

RegExp.prototype[@@match]()

[編集]

文字列に正規表現を適用し、一致する部分を検索します。String.prototype.match() によって間接的に呼び出されます。

const regex = /hello/g;
const str = "hello world, hello!";
console.log(str.match(regex)); // ["hello", "hello"]

RegExp.prototype[@@replace]()

[編集]

文字列に正規表現を適用し、一致部分を指定の文字列で置き換えます。String.prototype.replace() によって間接的に呼び出されます。

const regex = /world/;
const str = "hello world";
console.log(str.replace(regex, "universe")); // "hello universe"

RegExp.prototype[@@search]()

[編集]

文字列に正規表現を適用し、一致する最初の位置を返します。一致しない場合は -1 を返します。String.prototype.search() によって間接的に呼び出されます。

const regex = /world/;
const str = "hello world";
console.log(str.search(regex)); // 6

RegExp.prototype[@@split]()

[編集]

文字列を正規表現に一致する箇所で分割し、結果の配列を返します。String.prototype.split() によって間接的に呼び出されます。

const regex = /,\s*/;
const str = "apple, banana, cherry";
console.log(str.split(regex)); // ["apple", "banana", "cherry"]

PCREとRegExpオブジェクトの類似点と相違点

[編集]

PCRE(Perl Compatible Regular Expressions)とRegExpオブジェクト(JavaScriptで使用される正規表現)は、どちらも正規表現のエンジンですが、いくつかの類似点と相違点があります。以下にその主な点を挙げます。

類似点

[編集]
  1. 基本的な正規表現の構文
    両者は基本的な正規表現の構文(リテラル文字、メタ文字、量指定子、キャプチャグループ、バックスラッシュエスケープなど)を共有しています。
    例えば、a*(0回以上の'a')、(abc)(キャプチャグループ)、a|b(OR)などはPCREとRegExpの両方で同じ動作をします。
  2. フラグの使用
    両者はフラグ(オプション)を使用して、正規表現の挙動を変更できます。例えば、g(グローバル)、i(大文字小文字を区別しない)、m(複数行モード)などがこれに該当します。
  3. リテラルの一致
    両者は文字列に対してリテラルの一致を行う際、基本的な正規表現のパターンに基づいて一致を試みます。

相違点

[編集]
  1. エンジンの違い
    • PCREはPerlの正規表現エンジンを模倣したもので、Perlに近い動作をします。多くのプラットフォームで使用されており、例えばPHPやC言語でよく使用されます。
    • RegExpオブジェクトは、JavaScriptの標準ライブラリに組み込まれており、主にブラウザやNode.jsで使用されます。JavaScriptの正規表現エンジンはPCREに比べてやや制限されていることが多いです。
  2. サポートする機能
    • PCREはPerlに近い高度な機能をサポートしています。例えば、lookahead(先読み)やlookbehind(後読み)、条件付き正規表現、非キャプチャグループ(?:)、名前付きキャプチャグループ(?P<name>)など、非常に多くの高度な機能をサポートします。
    • RegExpオブジェクトは、lookaheadlookbehindに関しては一部制限がありますが、最近のバージョンでは追加されつつあります。#: その他の機能(例えば、named capture groupsなど)はRegExpでもサポートされており、より多くの機能がJavaScriptの進化に伴い追加されていますが、PCREに比べると機能が限定的です。
  3. フラグとオプション
    • PCREにはいくつかの独自のフラグやオプションが存在します。例えば、P(Perlモード)、U(Unicode対応)、X(拡張モード)などが挙げられます。
    • RegExpオブジェクトには、dotAll(ドットが改行を含む)やunicode(Unicodeサポート)など、PCREにはないフラグもあります。
  4. パフォーマンス
    • PCREは高性能な正規表現エンジンとして知られており、大規模なデータ処理でも効率的に動作します。C言語で書かれているため、非常に速く、高度な正規表現の最適化も行われています。
    • RegExpオブジェクトは、特にブラウザ環境で使われるため、多少パフォーマンスが劣る場合があります。ただし、Node.jsなどのサーバーサイドではPCREに匹敵するパフォーマンスを持つこともあります。
  5. Unicodeの扱い
    • PCREはUnicodeに強力なサポートを提供しており、Unicode文字クラスやUnicodeの正規表現を活用することができます。
    • RegExpオブジェクトは、Unicodeサポートが遅れて実装されましたが、最近ではuフラグを使ってUnicode文字を適切に扱えるようになっています。
  6. エラー処理
    • PCREはエラー処理が豊富で、正規表現のコンパイル時にエラーが発生すると詳細なエラーメッセージを提供します。
    • RegExpオブジェクトはエラー処理がシンプルで、無効な正規表現が与えられた場合には通常、SyntaxErrorをスローします。

まとめ

[編集]
PCREとRegExpの類似点と相違点
特徴 PCRE RegExpオブジェクト
基盤 Perlに基づく JavaScript専用
機能 高度な機能(先読み、後読み、条件付きなど) 基本的な正規表現機能(最新バージョンでは改善)
パフォーマンス 高速、最適化されたエンジン やや劣るが、最近では改善されている
Unicodeサポート 強力なUnicodeサポート 最近のバージョンでは改善
エラー処理 詳細なエラーメッセージ シンプルなエラー処理
フラグ 豊富なフラグ(PUXなど) 一部の独自フラグ(dotAllunicodeなど)

PCREは非常に強力で、特に複雑な正規表現を使用する際に役立ちます。一方で、RegExpは主にウェブ開発やJavaScript環境での利用が中心であり、使い勝手の良さや簡潔さが特徴です。どちらも用途に応じて使い分けることが重要です。

インスタンスメソッド

[編集]

RegExp.prototype.exec(string)

[編集]

引数で与えた文字列に正規表現を実行して、その結果を返す。 マッチする正規表現がなかった場合にはnullを返す。

const s = "abracadabra",
    regex = /a./g;
let ary = null;
while (ary = regex.exec(s)) {
  console.log(`${ary[0]} がマッチ、次のインデクスは ${regex.lastIndex}。`);
}
// ab がマッチ、次のインデクスは 2。
// ac がマッチ、次のインデクスは 5。
// ad がマッチ、次のインデクスは 7。
// ab がマッチ、次のインデクスは 9。

RegExp.prototype.test(string)

[編集]

指定された正規表現を実行して、マッチする文字があったかどうかを真偽値型で返す。

const s = "abracadabra";
if (/ab/g.test(s)){
  // ここから実際の処理
}

RegExp.prototype.toString()

[編集]

作成した正規表現をオプション付きで返す。

console.log(new RegExp( "ab", "ig" ).toString());	// 「/ab/gi」が表示される

下位階層のページ

[編集]

外部リンク

[編集]