第19章 正規表現
目次
書籍を購入する
(広告、ブロックしないでください。)

第19章 正規表現

この章では、正規表現のためのJavaScript APIの概要を説明します。正規表現がどのように機能するかについて、ある程度の知識があることを前提としています。もしそうでない場合は、Web上に優れたチュートリアルがたくさんあります。2つの例を挙げます。

正規表現の構文

ここで使用する用語は、ECMAScript仕様の文法をほぼ反映しています。理解しやすくするために、時々逸脱します。

アトム:一般

一般的なアトムの構文は次のとおりです。

特殊文字

次のすべての文字は特別な意味を持ちます。

\ ^ $ . * + ? ( ) [ ] { } |

バックスラッシュを前に付けることでエスケープできます。例えば

> /^(ab)$/.test('(ab)')
false
> /^\(ab\)$/.test('(ab)')
true

追加の特殊文字は次のとおりです。

  • 文字クラス [...] の内部

    -
  • 疑問符 (?...) で始まるグループの内部

    : = ! < >

    山括弧は、XRegExpライブラリでのみ使用されます(第30章を参照)、グループに名前を付けるために使用されます。

パターン文字
前述の特殊文字を除くすべての文字は、それ自体に一致します。
. (ドット)

改行文字(改行、キャリッジリターンなど)を除く任意のJavaScript文字(UTF-16コードユニット)に一致します。本当に任意の文字に一致させるには、[\s\S]を使用します。例えば

> /./.test('\n')
false
> /[\s\S]/.test('\n')
true
文字エスケープ(単一の文字に一致)
  • 特定の制御文字には、\f (フォームフィード)、\n (ラインフィード、改行)、\r (キャリッジリターン)、\t (水平タブ)、および\v (垂直タブ)が含まれます。
  • \0 はNUL文字(\u0000)に一致します。
  • 任意の制御文字:\cA\cZ
  • Unicode文字エスケープ:\u0000\xFFFF (Unicodeコードユニット;第24章を参照)。
  • 16進文字エスケープ:\x00\xFF
文字クラスエスケープ(文字のセットの1つに一致)
  • 数字:\d は任意の数字に一致します([0-9]と同じ)。\D は任意の非数字に一致します([^0-9]と同じ)。
  • 英数字:\w は任意のラテン英数字とアンダースコアに一致します([A-Za-z0-9_]と同じ)。\W\wに一致しないすべての文字に一致します。
  • 空白:\s は空白文字(スペース、タブ、ラインフィード、キャリッジリターン、フォームフィード、すべてのUnicodeスペースなど)に一致します。\S はすべての非空白文字に一致します。

アトム:文字クラス

文字クラスの構文は次のとおりです。

  • [«charSpecs»] は、少なくとも1つの charSpecs に一致する任意の単一の文字に一致します。
  • [^«charSpecs»] は、どの charSpecs にも一致しない任意の単一の文字に一致します。

次の構成はすべて文字仕様です。

  • ソース文字はそれ自体に一致します。ほとんどの文字はソース文字です(他の場所で特別な文字であっても)。そうでない文字は3つだけです。

        \ ] -

    通常どおり、バックスラッシュでエスケープします。エスケープせずにダッシュに一致させたい場合は、左括弧の直後の最初の文字であるか、後述する範囲の右側である必要があります。

  • クラスエスケープ:前にリストした文字エスケープおよび文字クラスエスケープのいずれも許可されています。追加のエスケープが1つあります。

    • バックスペース (\b):文字クラスの外側では、\b は単語の境界に一致します。文字クラス内では、制御文字のバックスペースに一致します。
  • 範囲は、ソース文字またはクラスエスケープ、その後にダッシュ (-)、その後にソース文字またはクラスエスケープで構成されます。

文字クラスの使用を説明するために、この例ではISO 8601標準でフォーマットされた日付を解析します。

function parseIsoDate(str) {
    var match = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(str);

    // Other ways of writing the regular expression:
    // /^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$/
    // /^(\d\d\d\d)-(\d\d)-(\d\d)$/

    if (!match) {
        throw new Error('Not an ISO date: '+str);
    }
    console.log('Year: '  + match[1]);
    console.log('Month: ' + match[2]);
    console.log('Day: '   + match[3]);
}

そして、これは相互作用です。

> parseIsoDate('2001-12-24')
Year: 2001
Month: 12
Day: 24

アトム:グループ

グループの構文は次のとおりです。

  • («pattern») はキャプチャグループです。pattern に一致するものは、後方参照または一致操作の結果としてアクセスできます。
  • (?:«pattern») は非キャプチャグループです。pattern は引き続き入力に対して一致しますが、キャプチャとして保存されません。したがって、グループには参照できる番号(例えば、後方参照経由)がありません。

\1\2 などは、後方参照として知られています。これらは、以前に一致したグループを参照します。バックスラッシュの後の数字は1以上の任意の整数にすることができますが、最初の数字は0であってはなりません。

この例では、後方参照はダッシュの前後のaの量が同じであることを保証します。

> /^(a+)-\1$/.test('a-a')
true
> /^(a+)-\1$/.test('aaa-aaa')
true
> /^(a+)-\1$/.test('aa-a')
false

この例では、後方参照を使用してHTMLタグに一致させます(明らかに、HTMLを処理するには通常、適切なパーサーを使用する必要があります)。

> var tagName = /<([^>]+)>[^<]*<\/\1>/;
> tagName.exec('<b>bold</b>')[1]
'b'
> tagName.exec('<strong>text</strong>')[1]
'strong'
> tagName.exec('<strong>text</stron>')
null

量指定子

任意のアトム(文字クラスやグループを含む)に、量指定子を続けることができます。

  • ? は、0回または1回一致することを意味します。
  • * は、0回以上一致することを意味します。
  • + は、1回以上一致することを意味します。
  • {n} は、正確に n 回一致することを意味します。
  • {n,} は、n 回以上一致することを意味します。
  • {n,m} は、少なくとも n 回、最大 m 回一致することを意味します。

デフォルトでは、量指定子は貪欲です。つまり、可能な限り多くの一致を探します。先行する量指定子(中括弧内の範囲を含む)のいずれかに疑問符(?)を付けることで、非貪欲な一致(可能な限り少なく)にできます。例えば:

> '<a> <strong>'.match(/^<(.*)>/)[1]  // greedy
'a> <strong'
> '<a> <strong>'.match(/^<(.*?)>/)[1]  // reluctant
'a'

したがって、.*? は、次のアトムが次に現れるまですべてに一致するのに便利なパターンです。例えば、次に示すのは、先ほど示したHTMLタグの正規表現(.*?の代わりに[^<]*を使用)のより簡潔なバージョンです。

/<(.+?)>.*?<\/\1>/

アサーション

次のリストに示すアサーションは、入力内の現在の位置に関するチェックです。

^

^

$

入力の先頭でのみ一致します。

$

入力の末尾でのみ一致します。

\b

単語の境界でのみ一致します。[\b] と混同しないでください。これはバックスペースに一致します。

\B

単語の境界にない場合のみ一致します。

(?=«pattern»)

肯定先読み:pattern が次に続くものと一致する場合のみ一致します。pattern は先読みするためだけに使用されますが、それ以外は無視されます。

(?!«pattern»)

> /\bell\b/.test('hello')
false
> /\bell\b/.test('ello')
false
> /\bell\b/.test('ell')
true

否定先読み:pattern が次に続くものと一致しない場合のみ一致します。pattern は先読みするためだけに使用されますが、それ以外は無視されます。

> /\Bell\B/.test('ell')
false
> /\Bell\B/.test('hell')
false
> /\Bell\B/.test('hello')
true

この例では、\b で単語境界に一致させます。

この例では、\B を使用して単語の内側に一致させます。

注記

後読みはサポートされていません。後読みの手動実装では、手動で実装する方法を説明します。

論理和

> /^aa|bb$/.test('aaxx')
true
> /^aa|bb$/.test('xxbb')
true

論理和演算子(|)は、2つの選択肢を区切ります。論理和が一致するには、どちらかの選択肢が一致する必要があります。選択肢はアトムです(オプションで量指定子を含みます)。

/^(aa|bb)$/

演算子は非常に緩やかにバインドするため、選択肢が広がりすぎないように注意する必要があります。例えば、次の正規表現は、aaで始まるか、bbで終わるすべての文字列に一致します。

/^a(a|b)b$/

言い換えれば、論理和は、^$よりもさらに緩やかにバインドされ、2つの選択肢は^aabb$です。'aa''bb'の2つの文字列に一致させるには、括弧が必要です。

同様に、'aab''abb'の文字列に一致させる場合は

Unicodeと正規表現

JavaScriptの正規表現では、Unicodeのサポートが非常に限られています。特にアストラルプレーンのコードポイントに関しては、注意が必要です。第24章で詳細を説明します。

正規表現の作成

リテラルまたはコンストラクターのいずれかで正規表現を作成し、フラグを使用してその動作を構成できます。

リテラルとコンストラクター

正規表現を作成するには、リテラルまたはコンストラクターRegExpを使用する2つの方法があります。

リテラル

/xyz/i

ロード時にコンパイルされます。

コンストラクター(2番目の引数はオプション)

new RegExp('xyz', 'i')

  • 実行時にコンパイルされます。

    function foo() {
        /[/;
    }
  • リテラルとコンストラクターは、コンパイルされるタイミングが異なります。

    function foo() {
        new RegExp('[');
    }

リテラルはロード時にコンパイルされます。次のコードは、評価されるときに例外を引き起こします。

したがって、通常はリテラルを使用する必要がありますが、正規表現を動的に組み立てたい場合は、コンストラクターが必要です。

フラグフラグは、正規表現リテラルのサフィックスであり、正規表現コンストラクターのパラメーターです。それらは正規表現の一致動作を変更します。次のフラグが存在します。短い名前

長い名前

説明

g

global

指定された正規表現が複数回一致します。いくつかのメソッド、特に replace() に影響します。

i

ignoreCase

指定された正規表現の一致を試みるときに、大文字と小文字が無視されます。

m

multiline

複数行モードでは、開始演算子 ^ と終了演算子 $ は、入力文字列全体ではなく、各行に一致します。

正規表現には、以下のインスタンスプロパティがあります。

  • フラグ: 設定されているフラグを示すブール値

    • global: /g フラグが設定されていますか?
    • ignoreCase: /i フラグが設定されていますか?
    • multiline: /m フラグが設定されていますか?
  • 複数回マッチングするためのデータ (フラグ /g が設定されている場合)

    • lastIndex は、次回検索を継続するインデックスです。

以下は、フラグのインスタンスプロパティにアクセスする例です。

> var regex = /abc/i;
> regex.ignoreCase
true
> regex.multiline
false

正規表現の作成例

この例では、まずリテラルで、次にコンストラクターで同じ正規表現を作成し、test() メソッドを使用して文字列と一致するかどうかを判断します。

> /abc/.test('ABC')
false
> new RegExp('abc').test('ABC')
false

この例では、大文字と小文字を区別しない正規表現を作成します (フラグ /i)

> /abc/i.test('ABC')
true
> new RegExp('abc', 'i').test('ABC')
true

RegExp.prototype.test: 一致するものがあるか?

test() メソッドは、正規表現 regex が文字列 str と一致するかどうかを確認します。

regex.test(str)

test() は、フラグ /g が設定されているかどうかによって動作が異なります。

フラグ /g が設定されていない場合、メソッドは str のどこかに一致するものがあるかどうかを確認します。例:

> var str = '_x_x';

> /x/.test(str)
true
> /a/.test(str)
false

フラグ /g が設定されている場合、メソッドは str 内の regex の一致の数だけ true を返します。プロパティ regex.lastIndex には、最後の一致後のインデックスが含まれています。

> var regex = /x/g;
> regex.lastIndex
0

> regex.test(str)
true
> regex.lastIndex
2

> regex.test(str)
true
> regex.lastIndex
4

> regex.test(str)
false

String.prototype.search: どこに一致するものがあるか?

search() メソッドは、str 内で regex との一致を検索します。

str.search(regex)

一致するものがある場合は、見つかったインデックスが返されます。それ以外の場合、結果は -1 です。検索が実行されるため、regex のプロパティ globallastIndex は無視されます (lastIndex は変更されません)。

例:

> 'abba'.search(/b/)
1
> 'abba'.search(/x/)
-1

search() の引数が正規表現でない場合は、正規表現に変換されます。

> 'aaab'.search('^a+b+$')
0

RegExp.prototype.exec: キャプチャグループ

次のメソッド呼び出しは、regexstr と照合しながらグループをキャプチャします。

var matchData = regex.exec(str);

一致するものがない場合、matchDatanull です。それ以外の場合、matchData は、2 つの追加プロパティを持つ一致結果である配列です。

配列要素
  • 要素 0 は、完全な正規表現の一致です (いわばグループ 0)。
  • 要素 n > 1 は、グループ n のキャプチャです。
プロパティ
  • input は、完全な入力文字列です。
  • index は、一致が見つかったインデックスです。

最初の一致 (フラグ /g が設定されていない場合)

フラグ /g が設定されていない場合、最初の一致のみが返されます。

> var regex = /a(b+)/;
> regex.exec('_abbb_ab_')
[ 'abbb',
  'bbb',
  index: 1,
  input: '_abbb_ab_' ]
> regex.lastIndex
0

すべての一致 (フラグ /g が設定されている場合)

フラグ /g が設定されている場合、exec() を繰り返し呼び出すと、すべての一致が返されます。戻り値 null は、一致がなくなったことを示します。プロパティ lastIndex は、次回の一致が継続される場所を示します。

> var regex = /a(b+)/g;
> var str = '_abbb_ab_';

> regex.exec(str)
[ 'abbb',
  'bbb',
  index: 1,
  input: '_abbb_ab_' ]
> regex.lastIndex
6

> regex.exec(str)
[ 'ab',
  'b',
  index: 7,
  input: '_abbb_ab_' ]
> regex.lastIndex
10

> regex.exec(str)
null

ここでは、一致をループ処理します。

var regex = /a(b+)/g;
var str = '_abbb_ab_';
var match;
while (match = regex.exec(str)) {
    console.log(match[1]);
}

次の出力が得られます。

bbb
b

String.prototype.match: キャプチャグループまたはすべての一致する部分文字列を返す

次のメソッド呼び出しは、regexstr と照合します。

var matchData = str.match(regex);

regex のフラグ /g が設定されていない場合、このメソッドは RegExp.prototype.exec() のように動作します。

> 'abba'.match(/a/)
[ 'a', index: 0, input: 'abba' ]

フラグが設定されている場合、メソッドは str 内のすべての一致する部分文字列 (つまり、すべての一致のグループ 0) を持つ配列を返します。一致するものがない場合は null を返します。

> 'abba'.match(/a/g)
[ 'a', 'a' ]
> 'abba'.match(/x/g)
null

String.prototype.replace: 検索と置換

replace() メソッドは、文字列 str 内で search との一致を検索し、それらを replacement で置換します。

str.replace(search, replacement)

2 つのパラメーターを指定する方法はいくつかあります。

検索

文字列または正規表現のいずれか。

  • 文字列: 入力文字列内でリテラルとして見つけられるもの。文字列の最初に出現する箇所のみが置換されることに注意してください。複数回出現する箇所を置換する場合は、/g フラグを使用して正規表現を使用する必要があります。これは予期せず、大きな落とし穴です。
  • 正規表現: 入力文字列と照合されるもの。警告: global フラグを使用してください。そうしないと、正規表現との一致を試みるのは 1 回のみになります。
置換

文字列または関数のいずれか。

  • 文字列: 見つかったものを置換する方法を記述します。
  • 関数: 置換を計算し、パラメーターを介して一致情報を取得します。

置換が文字列の場合

replacement が文字列の場合、その内容は一致箇所を置換するためにそのまま使用されます。唯一の例外は、ドル記号 ($) という特殊文字で、いわゆる置換ディレクティブを開始します:

  • グループ: $n は、一致箇所からグループ n を挿入します。n は 1 以上である必要があります ($0 には特別な意味はありません)。
  • 一致する部分文字列

    • $` (バッククォート) は、一致の前のテキストを挿入します。
    • $& は、完全に一致した箇所を挿入します。
    • $' (アポストロフィ) は、一致の後のテキストを挿入します。
  • $$ は、単一の $ を挿入します。

この例では、一致する部分文字列とその接頭辞および接尾辞を参照します。

> 'axb cxd'.replace(/x/g, "[$`,$&,$']")
'a[a,x,b cxd]b c[axb c,x,d]d'

この例では、グループを参照します。

> '"foo" and "bar"'.replace(/"(.*?)"/g, '#$1#')
'#foo# and #bar#'

置換が関数の場合

replacement が関数の場合、一致箇所を置換する文字列を計算します。この関数には、次のシグネチャがあります。

function (completeMatch, group_1, ..., group_n, offset, inputStr)

completeMatch は前述の $& と同じであり、offset は一致箇所が見つかった場所を示し、inputStr は照合対象です。したがって、特別な変数 arguments を使用してグループにアクセスできます (グループ 1 は arguments[1] など)。例:

> function replaceFunc(match) { return 2 * match }
> '3 apples and 5 oranges'.replace(/[0-9]+/g, replaceFunc)
'6 apples and 10 oranges'

フラグ /g に関する問題

正規表現の /g フラグが設定されている場合、その正規表現で呼び出されるメソッドがすべての結果を返すために複数回呼び出される必要がある場合は問題があります。これは、次の 2 つのメソッドの場合です。

  • RegExp.prototype.test()
  • RegExp.prototype.exec()

次に、JavaScript は、結果のシーケンスへのポインターとして、イテレーターとして正規表現を悪用します。これにより、問題が発生します。

問題 1: /g 正規表現はインライン化できない

例:

// Don’t do that:
var count = 0;
while (/a/g.test('babaa')) count++;

上記のループは無限になります。なぜなら、ループ反復ごとに新しい正規表現が作成され、結果の反復が再開されるからです。したがって、コードを書き換える必要があります。

var count = 0;
var regex = /a/g;
while (regex.test('babaa')) count++;

別の例を次に示します。

// Don’t do that:
function extractQuoted(str) {
    var match;
    var result = [];
    while ((match = /"(.*?)"/g.exec(str)) != null) {
        result.push(match[1]);
    }
    return result;
}

上記の関数を呼び出すと、再び無限ループが発生します。正しいバージョンは次のとおりです (lastIndex が 0 に設定される理由は後で説明します)。

var QUOTE_REGEX = /"(.*?)"/g;
function extractQuoted(str) {
    QUOTE_REGEX.lastIndex = 0;
    var match;
    var result = [];
    while ((match = QUOTE_REGEX.exec(str)) != null) {
        result.push(match[1]);
    }
    return result;
}

関数の使用

> extractQuoted('"hello", "world"')
[ 'hello', 'world' ]

ヒント

とにかくインライン化しないのがベストプラクティスです (そうすれば、正規表現にわかりやすい名前を付けることができます)。ただし、簡単なハックでも、それを行うことはできないことを認識する必要があります。

問題 2: パラメーターとしての /g 正規表現
test() および exec() を複数回呼び出すコードは、パラメーターとして渡された正規表現に注意する必要があります。フラグ /g はアクティブにする必要があり、安全のために、lastIndex をゼロに設定する必要があります (次の例で説明します)。
問題 3: 共有された /g 正規表現 (例: 定数)
新しく作成されていない正規表現を参照するときはいつでも、イテレーターとして使用する前に、lastIndex プロパティをゼロに設定する必要があります (次の例で説明します)。反復処理は lastIndex に依存するため、そのような正規表現は、同時に複数の反復処理で使用することはできません。

次の例は、問題 2 を示しています。これは、文字列 str 内の正規表現 regex の一致数をカウントする関数のナイーブな実装です。

// Naive implementation
function countOccurrences(regex, str) {
    var count = 0;
    while (regex.test(str)) count++;
    return count;
}

この関数の使用例を次に示します。

> countOccurrences(/x/g, '_x_x')
2

最初の問題は、正規表現の /g フラグが設定されていない場合、この関数が無限ループに入ることです。例:

countOccurrences(/x/, '_x_x') // never terminates

2 番目の問題は、regex.lastIndex が 0 でない場合、関数が正しく機能しないことです。これは、そのプロパティが検索を開始する場所を示すためです。例:

> var regex = /x/g;
> regex.lastIndex = 2;
> countOccurrences(regex, '_x_x')
1

次の実装では、2 つの問題を修正します。

function countOccurrences(regex, str) {
    if (! regex.global) {
        throw new Error('Please set flag /g of regex');
    }
    var origLastIndex = regex.lastIndex;  // store
    regex.lastIndex = 0;

    var count = 0;
    while (regex.test(str)) count++;

    regex.lastIndex = origLastIndex;  // restore
    return count;
}

より簡単な代替手段は、match() を使用することです。

function countOccurrences(regex, str) {
    if (! regex.global) {
        throw new Error('Please set flag /g of regex');
    }
    return (str.match(regex) || []).length;
}

落とし穴が 1 つあります。それは、/g フラグが設定されていて、一致がない場合、str.match()null を返すことです。前述のコードでは、match() の結果が真理値でない場合に [] を使用することで、その落とし穴を回避しています。

ヒントとコツ

このセクションでは、JavaScript で正規表現を操作するためのヒントとコツをいくつか紹介します。

テキストの引用

時々、正規表現を手動で組み立てる際、与えられた文字列をそのまま使用したい場合があります。つまり、特殊文字(例えば、*[)は特殊文字として解釈されるべきではなく、すべてエスケープされる必要があるということです。JavaScriptには、このようなクォーティングを行うための組み込み機能はありませんが、次のような動作をする独自の関数quoteTextをプログラムすることができます。

> console.log(quoteText('*All* (most?) aspects.'))
\*All\* \(most\?\) aspects\.

このような関数は、複数の出現箇所で検索と置換を行う必要がある場合に特に便利です。この場合、検索対象の値は、globalフラグが設定された正規表現である必要があります。quoteText()を使用すると、任意の文字列を使用できます。この関数は次のようになります。

function quoteText(text) {
    return text.replace(/[\\^$.*+?()[\]{}|=!<>:-]/g, '\\$&');
}

すべての特殊文字はエスケープされます。これは、括弧や角括弧内にある複数の文字をクォートしたい可能性があるためです。

落とし穴:アサーション(例:^、$)がない場合、正規表現はどこにでも見つかる

^$のようなアサーションを使用しない場合、ほとんどの正規表現メソッドはどこにでもパターンを見つけます。例えば:

> /aa/.test('xaay')
true
> /^aa$/.test('xaay')
false

すべてにマッチングまたは何にもマッチングしない

まれなケースですが、すべてにマッチングする、または何にもマッチングしない正規表現が必要になることがあります。例えば、関数がフィルタリングに使用される正規表現をパラメータとして持つ場合があります。そのパラメータが欠落している場合、デフォルト値として、すべてにマッチングする正規表現を与えます。

すべてにマッチングする

空の正規表現はすべてにマッチングします。次のように、その正規表現に基づいてRegExpのインスタンスを作成できます。

> new RegExp('').test('dfadsfdsa')
true
> new RegExp('').test('')
true

ただし、空の正規表現リテラルは//となり、JavaScriptではコメントとして解釈されます。したがって、リテラルで最も近いものは、/(?:)/(空の非キャプチャグループ)です。このグループはすべてにマッチングしますが、何もキャプチャせず、exec()によって返される結果に影響を与えません。JavaScript自体も、空の正規表現を表示するときに上記の表現を使用します。

> new RegExp('')
/(?:)/

何もマッチングしない

空の正規表現には逆があります。それは、何もマッチングしない正規表現です。

> var never = /.^/;
> never.test('abc')
false
> never.test('')
false

先読みの手動実装

先読みはアサーションです。先読みと同様に、パターンを使用して入力内の現在の位置について何かを確認しますが、それ以外は無視されます。先読みとは対照的に、パターンのマッチは、現在の位置で終了する必要があります(そこから開始するのではなく)。

次の関数は、文字列'NAME'の各出現箇所をパラメータnameの値に置き換えますが、その出現箇所の前に引用符がない場合に限ります。引用符は、現在のマッチの前の文字を「手動で」確認することで処理します。

function insertName(str, name) {
    return str.replace(
        /NAME/g,
        function (completeMatch, offset) {
            if (offset === 0 ||
                (offset > 0 && str[offset-1] !== '"')) {
                return name;
            } else {
                return completeMatch;
            }
        }
    );
}
> insertName('NAME "NAME"', 'Jane')
'Jane "NAME"'
> insertName('"NAME" NAME', 'Jane')
'"NAME" Jane'

もう1つの方法は、正規表現でエスケープする可能性のある文字を含めることです。次に、検索対象の文字列に一時的にプレフィックスを追加する必要があります。そうしないと、その文字列の先頭にあるマッチを見逃す可能性があります。

function insertName(str, name) {
    var tmpPrefix = ' ';
    str = tmpPrefix + str;
    str = str.replace(
        /([^"])NAME/g,
        function (completeMatch, prefix) {
            return prefix + name;
        }
    );
    return str.slice(tmpPrefix.length); // remove tmpPrefix
}

正規表現チートシート

アトム(「アトム:一般」を参照)

  • .(ドット)は、改行文字(例えば、改行)を除くすべてにマッチングします。本当にすべてにマッチングするには、[\s\S]を使用します。
  • 文字クラスエスケープ

    • \dは数字([0-9])にマッチングします。\Dは非数字([^0-9])にマッチングします。
    • \wはラテン文字の英数字とアンダースコア([A-Za-z0-9_])にマッチングします。\Wは他のすべての文字にマッチングします。
    • \sはすべての空白文字(スペース、タブ、改行など)にマッチングします。\Sはすべての非空白文字にマッチングします。
  • 文字クラス(文字の集合):[...][^...]

    • ソース文字:[abc]\ ] -を除くすべての文字は自分自身にマッチングします)
    • 文字クラスエスケープ(上記参照):[\d\w]
    • 範囲:[A-Za-z0-9]
  • グループ

    • キャプチャグループ:(...)、後方参照:\1
    • 非キャプチャグループ:(?:...)

量指定子(「量指定子」を参照)

  • 欲張り

    • ? * +
    • {n} {n,} {n,m}
  • 怠惰:欲張り量指定子の後に?を付けます。

アサーション(「アサーション」を参照)

  • 入力の先頭、入力の末尾:^ $
  • 単語境界、単語境界ではない:\b \B
  • 肯定先読み:(?=...)(パターンが次に来る必要があり、それ以外は無視されます)
  • 否定先読み:(?!...)(パターンが次に来てはならず、それ以外は無視されます)

選言:|

正規表現の作成(「正規表現の作成」を参照)

  • リテラル:/xyz/i(ロード時にコンパイル)
  • コンストラクター:new RegExp('xzy', 'i')(実行時にコンパイル)

フラグ(「フラグ」を参照)

メソッド

/gフラグの使用に関するヒントについては、「フラグ /g に関する問題」を参照してください。

謝辞

Mathias Bynens (@mathias) と Juan Ignacio Dopazo (@juandopazo) は、出現回数を数えるためにmatch()test()を使用することを推奨し、Šime Vidas (@simevidas) は、マッチがない場合にmatch()を使用する際に注意するよう警告しました。グローバルフラグが無限ループを引き起こすという落とし穴は、Andrea Giammarchi (@webreflection) の講演から得られました。Claude Pache は、quoteText()でより多くの文字をエスケープするようにと教えてくれました。

次へ:20. 日付