リテラシーのためにフロントエンドで収集する価値のある正規表現の知識ポイント

多くのフロントエンド初心者は、正規表現に遭遇すると怖気づいてしまいます。私が最初に学習し始めたとき、私は基本的に正規表現の章を飛ばしていました。フォーム検証のためにインターネット上で一般的に使用されているいくつかの正規表現をコピーしたことを除いて、残りの時間はほとんど理解できました。正規表現の書き方が全く分かりませんでした。

しかし、特定のビジネスに適した正規表現を本当に書きたいと思ったとき、私の正規表現に関する知識は非常に限られていることがわかりました。そこで、ここでも正規表現について知っておくべき知識ポイントをマインドマップを使って整理してみました。

正規表現とは何ですか

正規表現。正規表現とも呼ばれます。(英語: Regular Expression、コード内では regex、regexp、または RE と略されることがよくあります)、コンピューター サイエンスの概念。正規表現は、特定のパターン (ルール) に一致するテキストを取得および置換するためによく使用されます。

ソフトウェア開発のプロセスでは、多かれ少なかれ正規表現にさらされることがありますが、フロントエンドでは、正規表現はフォームの検証、テキストの検索と置換だけでなく、構文パーサー、AST、エディターとしても使用できます。そして他の分野。

正規表現

直接表現

直接量はリテラルとも呼ばれ、次のように記述されます。

/^\d+$/g

RegExp直接形式で記述された正規表現は、実行時に新しいオブジェクトに変換されます。直接変数にはメソッドを呼び出す機能がなく、オブジェクト化して初めてメソッドを呼び出せるようになるからだと思います。それが理由です/^\d+$/.test()

終了条件を決定するためにループ内で正規オブジェクトを使用する場合はlastIndex、リテラル正規表現の書き込みを使用しないようにしてください。そうしないと、各ループがlastIndexにリセットされます0。これは、リテラル正規表現が実行されるたびに、リテラル正規表現が に変換されるためです。もちろん、新しいオブジェクトはRegExp対応するようになりますlastIndex0

RegExp オブジェクトの表記法

var pattern = new RegExp(/^\d+$/, 'g')

最初のパラメータは、正規表現リテラルまたは文字列を受け入れることができます。文字列を最初のパラメータとして渡す場合、先頭または末尾にスラッシュは必要ありません。文字列で特殊文字が使用されている場合は\、その\前に one. 、文字列内でエスケープされるのを\防ぐため。\

"\s" === "s" // true

文字列"\\s"を正しく表現できる\s

2 番目のパラメータはフラグを表しますflags。使用可能なフラグはigmなどです。

フラグ

このフラグが有効な場合i、正規表現は大文字と小文字を区別せずに実行されます。

/abc/i.test('abc')等价于/abc/i.test('ABC')

g

このフラグが有効な場合g、正規表現はグローバル照合を実行し、照合ルールに一致する後続の文字がなくなるまで、結果の照合直後に照合を停止しません。

メートル

フラグが有効な場合m、正規表現は複数行の一致を実行します。これにより、^各行の先頭または文字列全体の先頭と一致したり、$各行の末尾または文字列全体の末尾と一致したりすることができます。

/^\d+$/.test('123\n456') // false /^\d+$/m.test('123\n456') // true

引き続き文字列全体と一致する可能性があります

/^\d+\n\d+$/m.test('123\n45') // true

位置修飾子

^

文字の先頭と一致します。たとえば、数字で始める必要がある場合は、次のように記述できます。

/^\d/

$

一致した文字の末尾。たとえば、数字で終わる必要がある場合は、次のように記述できます。

/\d$/

範囲一致

範囲マッチングは角括弧を使用して[]実装されます。

角かっこは[]、特定の範囲内の文字を検索する範囲一致に使用されます。たとえば、[0-9]これは数字の一致を意味しますが、[a-z]小文字の a から z までの 26 文字のいずれかと一致する可能性があります。

角括弧内にない文字を一致させたい場合は、角括弧内で開始することができます (^例: ) [^0-9]。数字以外の文字と一致します。これは と同等です\D

プライマリメタキャラクター

改行文字を除く任意の文字に一致します\n。任意の文字に一致させたい場合は、 を使用する必要があります/[.\n]*/

\s

\tスペース、タブ、垂直タブ\v、ラインフィード\n、キャリッジリターン、\rフォームフィードを含む任意の空文字と一致します\f\sと同等ですが[ \t\v\n\r\f]、角括弧内の最初の位置にスペースがあることに注意してください。

ここでは、改行文字と復帰文字の違いについても説明します。

  • 改行\n: 行頭に戻らずにカーソルを 1 行下に移動します。
  • 復帰文字\r: カーソルは改行せずに行の先頭に戻ります。

\S

\Sはい\s、 の逆集合であり、と の相互逆関係を使用すると、次のように記述された任意の文字と一致できます\s\S

/[\s\S]/

\d

\d数値を照合するために使用され、 と同等です[0-9]

\D

\Dはい\d、逆集合、つまり数値以外の一致は と同等です[^0-9]

\w

\w0-9、 、およびアンダースコアを含む単語文字の一致に使用されa-zと同等ですA-z_[A-Za-z0-9_]

\W

\W\w単語以外の文字と一致するために使用される逆セットであり、 と同等です[^A-Za-z0-9_]

\n

\n開発中によく遭遇する改行文字であり、上記のものが\s含まれます\nしたがって、\n一致する文字も\s一致する必要があります。

\b

\b単語の境界、つまり単語の先頭または末尾を一致させるために使用されます。

実際、最初は\b正規表現におけるその役割をよく理解していませんでした。

私がこのケースを自分で試すまでは

 
 

コードをコピーする

'I love you'.match(/love/) 'Iloveyou'.match(/love/)

どちらの式も結果と一致します"love"

'Iloveyou'ただし、単語間にスペースがないため、このような文字列を一致させたくない場合があります。

だからこそ\b存在意義があるのです。次の例を見てください。

 
 

コードをコピーする

'I love you'.match(/\blove\b/) 'Iloveyou'.match(/\blove\b/) // null

最初の式は結果と通常どおり一致しますが、2 番目の式は結果と一致できません。これは期待どおりです。

それならスペースを使って一致させればいいじゃないか、と言う人もいるかもしれません。

 
 

コードをコピーする

'I love you'.match(/ love /)

このシナリオではスペースが\bまだ少し異なり、それがmatch結果に反映されています。

一致させるためにスペースを使用する場合、match結果の配列の最初の項目には" love "スペースが含まれますが、多くの場合、結果にスペースを含めたくないため、\b存在の意味がより明確になります。

\B

反対\bは、非単語境界を表します。つまり、\Bマッチングを使用する場合、ターゲット文字の前後にスペースを入れることはできません。

\Bたとえば、最初に仮定します

/\Babc/.test('111 abc') // false

\Bたとえば後で仮定すると、

/abc\B/.test('abc 111') // false

エスケープ文字\

(正規表現内の多くの文字には、 、)\[]などの特別な意味があるため、+それらを本当に一致させたい場合は、エスケープ文字を追加する必要があります\

/\//.test('/'); // true

または |

OR を実装するロジックは比較的単純で、正規表現によって提供されます|

|分離されるのは、通常の 1 文字ではなく、その左右の部分式全体であることに注意してください。

それで、

/^ab|cd|ef$/.test('ab') // true 
/^ab|cd|ef$/.test('cd') // true 
/^ab|cd|ef$/.test('ace') // false

また、|左から右への優先順位があるため、左側が一致する場合、右側の一致がより「完璧」に見えても、右側は無視されることにも注意してください。

/a|ab/.exec('ab')得到的结果是

["a", index: 0, input: "ab", groups: undefined]

数量詞

?

直前の部分式と 0 回または 1 回一致します。

+

前の部分式と 1 回以上一致します

*

直前の部分式と 0 回または任意の回数一致します。

{n,m}

前の通常の文字または部分式と少なくとも n 回、最大で m 回一致します

{n,}

前の通常の文字または部分式と少なくとも n 回一致します

{n}

前の通常の文字または部分式と n 回一致します

よく深い

貪欲マッチングとは、可能な限りマッチングすることであり、マッチング条件を満たすことができれば、その後のマッチングルールを可能な限り占有します。

貪欲な一致がデフォルトです。たとえば、できるだけ多くの数値を/\d?/一致させたりできるだけ多くの数値を一致させたりします。1/\d+//\d*/

例えば、

'123456789'.match(/^(\d+)(\d{2,})$/)

上記の結果では、キャプチャ グループの最初の項目は で"1234567"、2 番目の項目は です"89"

なぜそうなるのでしょうか? 貪欲一致なので\d+、できるだけ多く一致します。後続がない場合は\d{2,}、キャプチャ グループの最初のアイテムが直接 になります"123456789"\d{2,}しかし、 の存在により、面子\d+\d{2,}保たれ、その最小条件、つまり 2 つの数字と一致し、\d+7 つの数字と単独で一致します。

貪欲ではない

非貪欲マッチングとは、可能な限り一致を少なくすることです。通常、数量詞?,の後に 1 を追加して、一致を可能な限り少なくし、後続の一致ルールに機会を残します+*?

貪欲モードの例を取り上げて、それを\d+非貪欲モードに少し変更してみましょう\d+?

'123456789'.match(/^(\d+?)(\d{2,})$/)

キャプチャ グループの最初の項目は で"1"、2 番目の項目は になります"23456789"

なぜそうなるのでしょうか? 非貪欲モードでは、一致が可能な限り少なくなり、後続の一致ルールに機会が残されるためです。

グループ

グループ化は、正規表現において非常に便利な成果物です。()括弧で囲まれた内容はグループです。正規表現では、次の形式で表現されます。

/(\d*)([a-z]*)/

キャプチャグループ()

キャプチャ グループを使用すると、主要なキャラクターをキャプチャできます。

例えば

var group = '123456789hahaha'.match(/(\d*)([a-z]*)/)

グループ 1 は任意の数の数字と一致するために使用され、グループ 2 は任意の数の小文字と一致するために使用されます。

次に、matchメソッドの戻り結果、group[1]yes "123456789"group[2]yesでこれら 2 つのグループの一致する結果を取得できます"hahaha"

RegExp静的属性で$1~$9前のグループ一致の結果を取得することもできます9RegExp.$1はいはい"123456789"RegExp.$2_ "hahaha"_ ただしRegExp.$1~$9、これは標準ではないため、多くのブラウザで実装されていますが、運用環境では使用しないようにしてください。

この種のキャプチャ グループのアプリケーションは文字列replaceメソッドに似ていますが、replaceメソッドを呼び出すときは$1、この形式$2を通じて$nグループを参照する必要があります。

"123456789hahaha".replace(/(\d*)([a-z]*)/, "$1") // "123456789"

を使用すると$1、ソース文字列をグループ 1 に一致する文字列、つまり に置き換えることができます"123456789"

非キャプチャグループ(?:)

非捕捉グループとは参照を生成しないグループで、これも()括弧で囲まれていますが、括弧の先頭が?:、つまり/(?:\d*)/この形になります。

前の例を見てみましょう。

var group = '123456789hahaha'.match(/(?:\d*)([a-z]*)/)

非キャプチャ グループは参照を生成しないため、 ですgroup[1]"hahaha"同様に、RegExp.$1です"hahaha"

これを見ると、非キャプチャ グループを参照する必要がないのに、非キャプチャ グループに何の意味があるのか​​と疑問に思わずにはいられません。

しばらく考えてみたところ、非捕獲グループには次のような利点と必要性があると思います。

  1. 非キャプチャ グループは参照を生成する必要がないため、キャプチャ グループよりもメモリの使用量が少なくなります。

  2. グループ化は、量指定子を追加する際の便宜のためです。参照を生成することはできませんが、グループ化がない場合、文字のグループに量指定子を追加するのは不便です。

'1a2b3c...'.match(/(?:\d[a-z]){2,3}(\.+)/)

引用\num

正規表現は参照を使用して前のグループを参照できます。この形式では\1\2前の部分式を参照できます。

たとえば、文字列を一致させたい場合は、次のルールを満たす必要があります。

文字列は一重引用符または二重引用符で始まり、終わり、中間の内容は数字または単語になります。

次に、最初と最後が一重引用符または二重引用符であることを確認したいので、パターンは次のように記述できます。

var pattern = /^(["'])[a-z\d]*\1$/ pattern.test("'perfect123'") // true 

pattern.test('"1perfect2"') // true

ゼロ幅アサーション

正直に言うと、最初にゼロ幅アサーションの概念と説明を見たときは、何を言っているのかよくわかりませんでした。

  • ゼロ幅の前方先読みアサーション (?=)
  • ゼロ幅の否定先読みアサーション(?!)
  • ゼロ幅の正の後読みアサーション (?<=)
  • ゼロ幅の負の後方アサーション (?<!)

その後、単語を分解して自分なりの理解を加えていくと、少しずつ理解できるようになりました。

  • ゼロ幅: ゼロ幅、アサーションはマッチングの必須条件として使用されますが、マッチング結果には反映されません。
  • 正: 正、アサーション内の文字が一致する必要があります。
  • 否定: 否定、アサーション内の文字を一致させることができません。
  • 先読み: 先読みは前方の条件を満たさなければなりません。条件は前方にあり、前方は右側に等しいです。
  • 後列: 後ろを見て、後ろの条件を満たす必要があります。条件は後方にあり、後ろは左側に等しいです。

ゼロ幅の前方先読みアサーション (?=)

指定された文字は、制約ターゲットの右側に存在する必要があります。

/123(?=a)/.test('123a') // true

上の例では、123右側が存在するように制約されていますa

ゼロ幅の否定先読みアサーション(?!)

指定された文字は拘束対象の右側に存在できません。

/123(?!a)/.test('123a') // false

上の例では、123右側に何も存在できないことを制約していますa。そうでない場合、結果は次のようになりますfalse

ゼロ幅の正の後読みアサーション (?<=)

指定された文字は、制約ターゲットの左側に存在する必要があります。

/(?<=a)123/.test('a123') // true

上の例では、123左側が存在するように制約されていますa

ES2018 のみがゼロ幅の改行アサーションをサポートします

ゼロ幅の負の後方アサーション (?<!)

指定された文字は拘束対象の左側に存在できません。

/(?<!a)123/.test('a123') // false

上の例では、123左側に何も存在できないことを制約していますa。そうでない場合、結果は次のようになります。false

注: この機能は ES2018 でのみサポートされています。

正規表現

正規表現に関して言えば、RegExpオブジェクトについて言及する必要があります。次に、プロトタイプ メソッド、静的プロパティ、インスタンス プロパティなどのいくつかの側面からオブジェクトについて学びますRegExp

プロトタイプメソッド

RegExp.prototype.test

test()これは最も一般的に使用される正規メソッドです。test()このメソッドは、正規表現が指定された文字列と一致するかどうかを確認するために取得を実行し、ブール値trueまたはを返しますfalse

正規表現にグローバル フラグが設定されている場合g、実行により、最後に一致した文字の開始インデックスを記録するために使用される属性test()が変更されます。RegExp.lastIndexメソッドが継続的に実行される場合test()、後続の実行はlastIndexこの時点から始まる文字列と一致します。この場合、test()結果が一致しない場合はlastIndexにリセットされます0

RegExp.prototype.exec

exec()より豊富な一致情報を取得する場合と比較してtest()、結果は配列であり、配列の 0 番目の要素が一致した文字列であり、1 番目から n 番目の要素は()括弧でグループ化された結果になります。

結果の配列は配列であり、その配列はオブジェクト型データでもあるため、結果の配列には次の 2 つの属性もありますindexinput

  • index元の文字列内の一致した文字の 0 から始まるインデックス値を表します
  • input元の文字列を表します

test()正規表現と一致して、フラグが設定されている場合、フラグはg実行のたびにexec()更新されますlastIndex

静的プロパティ

静的プロパティはどのインスタンスにも属さないため、クラス名を通じてアクセスする必要があります。

正規表現$1-$9

グループのマッチング結果を取得するために使用され、RegExp.$1最初のグループのマッチング結果が取得され、RegExp.$99 番目のグループのマッチング結果が取得されます。

インスタンスのプロパティ

lastIndex

lastIndex意味的に理解されると、最後に一致した文字の開始インデックスです。gこれはフラグが設定されている場合にのみ有効であることに注意してくださいlastIndex

まだ一致しない場合は、lastIndex当然、0番目の文字列から一致することになります0

lastIndexexec()としてtest()更新されます

var reg = /\d/g reg.lastIndex // 0 

reg.test('123456') reg.lastIndex // 1 

reg.exec('123456') reg.lastIndex // 2

lastIndex手動で変更できるため、試合の詳細を自由に制御できます。

フラグ

flagsプロパティは、この正規表現インスタンスに対してどのフラグが有効になっているかを表す文字列を返します。

var reg = /\d/ig reg.flags; // "gi"

グローバル

global正規表現がgフラグを使用するかどうかを示すブール値です。

無視するケース

ignoreCase正規表現がiフラグを使用するかどうかを示すブール値です。

複数行

multiline正規表現がmフラグを使用するかどうかを示すブール値です。

ソース

sourceソースを意味する、正規表現の文字列表現であり、通常のリテラルやフラグ文字の両側にスラッシュは含まれません。

規則性を伴う文字列メソッド

文字列.プロトタイプ.検索

search()このメソッドは、正規表現を使用して文字列オブジェクトの照合を実行し、index文字列内の正規表現の最初の一致を表すインデックスを返します。一致するものが見つからない場合は、 を返します-1

search()メソッドのパラメータは正規表現である必要があります。そうでない場合は、new RegExp()暗黙的に正規表現オブジェクトに変換されます。

"123abc".search(/[a-z]/); // 3

文字列.プロトタイプ.マッチ

stringメソッドは文字列を取得するために使用され、match正規表現メソッドexecに似ています。matchメソッドのパラメータも正規表現である必要があります。matchメソッドは配列を返します。

exec()と の違いは、matchメソッドによって渡された正規表現がの場合g、完全な正規表現に一致するすべての結果が返されますが、キャプチャ グループは返されないことです。

"123abc456".match(/([a-z])/g); 

// 返回["a", "b", "c"] 
var reg = /([a-z])/g; reg.exec('123abc456');

// 返回数组
["a", "a", index: 3, input: "123abc456", groups: undefined] reg.exec('123abc456'); 

// 返回数组
["b", "b", index: 4, input: "123abc456", groups: undefined] reg.exec('123abc456'); 

// 返回数组
["c", "c", index: 5, input: "123abc456", groups: undefined]

match()メソッドによって渡された正規表現に flag がない場合g、動作はexec()メソッドと一貫しており、最初に一致した結果とグループ化されたキャプチャ結果のみが返されます。

このとき、式中に括弧でグループ化されている場合は、match()そのグループ化の一致結果も結果配列で取得できますが、これについてもキャプチャグループで説明します。

"123abc456".match(/([a-z])/); // 返回["a", "a", index: 3, input: "123abc456", groups: undefined] RegExp.$1; // "a"

文字列.プロトタイプ.置換

replace()文字列置換メソッドです。最初のパラメータが正規表現である必要はありません。最初のパラメータが正規表現でグループ化を含む場合、2 番目のパラメータでグループ化の一致結果をこの形式replace()参照できます"$1""$2"

"123456789hahaha".replace(/(\d*)([a-z]*)/, "$1") // "123456789"

文字列.prototype.split

split()この方法は文字列分割方法であり、これも一般的に使用される方法ですが、パラメータとして正規表現を受け入れることができることを知らない人も多いです。

このような不規則な文字列を取得し"1,2, 3 ,4, 5"、その文字列を分割して純粋な数値の配列を取得する必要があるとします。split(",")これを直接使用することはできませんが、正規表現を分割条件として使用することで実行できます。

var str = "1,2, 3 ,4, 5"; str.split(/\s*,\s*/); // 返回 ["1", "2", "3", "4", "5"]

やっと

正規表現は非常に重要ですが見落としがちな知識であり、面接でも頻繁に試験される項目ですので十分に注意する必要があります。以上の知識を整理しておけば、その後の実戦でも自信を持って冷静に対応できると思います。

厳選されたプロジェクト ソース コード 250 セットが付属

ソースコードのスクリーンショット

ソース コードの取得: 公開アカウント「Coder Park」をフォローし、[ソース コード] と返信すると、ソース コードの完全なダウンロード リンクが取得できます。

おすすめ

転載: blog.csdn.net/lwzhang1101/article/details/135401372