普通の人のためのJavaScript正規表現
正規表現は、regexまたはregexpとも呼ばれ、取り組むのが難しい問題です。 独自の正規表現を書くことにまだ100% c慣れていなくても、恥ずかしがらないでください。慣れが必要です。 この記事の終わりまでに、Stack Overflowのコピーパスタにそれほど依存することなく、JavaScriptで独自の式を揺るがすことができるようになることを願っています。
正規表現を作成するための最初のステップは、正規表現を呼び出す方法を理解することです。 JavaScriptでは、正規表現は標準の組み込みオブジェクトです。 このため、新しいものを作成できます RegExp
いくつかの方法でオブジェクト:
- 文字通りの方法、
/expression/.match('string to test against')
- The
new
文字列引数を持つキーワード、new RegExp('expression')
- The
new
リテラルを含むキーワード、new RegExp(/expression/)
これらのメソッドを組み合わせて使用して、基本的に同じジョブを実行することを示します。
正規表現の目標
私の例では、名、名前、ドメイン名を含む文字列を使用します。 現実の世界では、この例にはもっと多くのことを考える必要があります。 名前の扱いに関しては、微妙な点がたくさんありますが、ここでは取り上げません。
ダッシュボードを作成していて、ログインしているユーザーの名前を表示したいとします。 私は自分に返されるデータを制御できないので、自分が持っているものでやらなければなりません。
変換する必要があります aaron.arney:alligator.io
の中へ Aaron Arney [Alligator]
.
正規表現は、多くのロジックを1つの凝縮されたオブジェクトに適合させます。 これは混乱を引き起こす可能性があります。 式を疑似コードの形式に分解することをお勧めします。 これにより、何がいつ発生する必要があるかを確認できます。
- 名を抽出する
- 姓を抽出する
- ドメイン名を抽出する
- 文字列を目的のテンプレート形式にフォーマットします
First Last [Domain]
名の一致
文字列を正規表現と一致させるには、リテラル文字列を渡すだけです。 The i
式の最後にはフラグがあります。 The i
特に旗は case insensitive
. これは、文字列の大文字小文字を無視する式を意味します。
const unformattedName = 'aaron.arney:alligator.io';
const found = unformattedName.match(/aaron/i);
console.log(found);
// expected output: Array [ "aaron" ]
これはうまく機能しますが、ユーザーの名前が常に「アーロン」になるとは限らないため、この場合は適切なアプローチではありません。 ここで、プログラムで一致する文字列を調べます。
とりあえず、名のマッチングに焦点を当てましょう。 単語を個々の文字に分解します、あなたは何を見ますか?
「アーロン」という名前は、5つの英字で構成されています。 すべての名はのみ5文字ですか? いいえ。ただし、名の範囲は1〜15文字であると想定するのが妥当です。 azの範囲内の文字を表すために、 [a-z]
.
ここで、この文字クラスを使用するように式を更新すると…
const unformattedName = 'aaron.arney:alligator.io';
const found = unformattedName.match(/[a-z]/i);
console.log(found);
// expected output: Array [ "a" ]
文字列から「aaron」を抽出する代わりに、「a」のみを返します。 正規表現は可能な限り一致させないようにするため、これは良いことです。 制限の15までの数字と一致する文字を繰り返すには、中括弧を使用します。 これは、前のトークンである「az」と1〜15回一致するように監視している式を示しています。
const unformattedName = 'aaron.arney:alligator.io';
const unformattedNameTwo = 'montgomery.bickerdicke:alligator.io';
const unformattedNameThree = 'a.lila:alligator.io';
const exp = new RegExp(/[a-z]{1,15}/, 'i');
const found = unformattedName.match(exp);
const foundTwo = unformattedNameTwo.match(exp);
const foundThree = unformattedNameThree.match(exp);
console.log(found);
// expected output: Array [ "aaron" ]
console.log(foundTwo);
// expected output: Array [ "montgomery" ]
console.log(foundThree);
// expected output: Array [ "a" ]
姓の一致
姓の抽出は、最初の式をコピーして貼り付けるのと同じくらい簡単です。 一致すると、名前と名前の両方ではなく、同じ値が返されることに気付くでしょう。
文字列を文字ごとに分割すると、名前を完全に区切ることができます。 これを説明するために、式に終止符を追加します。
ここでは注意が必要です。 The .
式の2つのことのうちの1つを意味することができます。
.
-改行以外の任意の文字に一致します\.
– 抹茶 。
このコンテキストでいずれかのバージョンを使用すると、同じ結果が生成されますが、常にそうであるとは限りません。 eslint のようなツールは、エスケープシーケンスをマークすることがあります \
不必要ですが、申し訳ありませんが安全だと言っています。
const unformattedName = 'aaron.arney:alligator.io';
const exp = new RegExp(/[a-z]{1,15}\.[a-z]{1,15}/, 'i');
const found = unformattedName.match(exp);
console.log(found);
// expected output: Array [ "aaron.arney" ]
文字列を2つの項目に分割し、式によってピリオドが返されないようにすることを好むため、次を使用できます。 capturing groups
. これらは括弧で示されます ()
そして、あなたが返されたいあなたの表現の部分を包み込みます。 それらを名前式と姓式にラップすると、新しい結果が得られます。
キャプチャグループを使用するための構文は単純です。 (expression)
. 姓と名のみを返したいので、
const unformattedName = 'aaron.arney:alligator.io';
const exp = new RegExp(/([a-z]{1,15})\.([a-z]{1,15})/, 'i');
const found = unformattedName.match(exp);
console.log(found);
// expected output: Array [ "aaron.arney", "aaron", "arney" ]
ドメイン名の照合
「alligator.io」を抽出するために、これまでに使用した文字クラスを使用します。 もちろん、少し変更を加えます。
ドメイン名とTLDの検証は難しいビジネスです。 解析するドメインのふりをしますが、常に > 3 && < 25
文字。 TLDは常に > 1 && < 10
. これらを接続すると、新しい出力が得られます。
const unformattedName = 'aaron.arney:alligator.io';
const exp = new RegExp(/([a-z]{1,15})\.([a-z]{1,15}):([a-z]{3,25}\.[a-z]{2,10})/, 'i');
const found = unformattedName.match(exp);
console.log(found);
// expected output: Array [ "aaron.arney:alligator.io", "aaron", "arney", "alligator.io" ]
ショートカット
表現の「長い道のり」をお見せしました。 ここで、同じテキストをキャプチャする、より冗長でない表現を作成する方法を紹介します。 を使用して +
数量詞を使用すると、前のトークンをできるだけ多く繰り返すように式に指示できます。 行き止まりに達するまで、この場合は終止符に達するまで続きます。 この表現はまた、 g
フラグ、 global
. これは、検索を最短回数ではなく、可能な限り繰り返したいという表現を示しています。
// With the global flag
'aaron.arney:alligator.io'.match(/[a-z]+/ig);
// expected output: Array(4) [ "aaron", "arney", "alligator", "io" ]
// Without the global flag
'aaron.arney:alligator.io'.match(/[a-z]+/i);
// expected output: Array(4) [ "aaron" ]
出力のフォーマット
文字列をフォーマットするには、replaceメソッドを使用します。 String
物体。 The replace
メソッドは2つの引数を取ります:
RegExp | String
-正規表現オブジェクトまたはリテラルRegExp | function
-正規表現または関数
const unformattedName = 'aaron.arney:alligator.io';
// The "long" way
const exp = new RegExp(/([a-z]{1,15})\.([a-z]{1,15}):([a-z]{3,25}\.[a-z]{2,10})/, 'i');
unformattedName.replace(exp, '$1 $2 [$3]');
// expected output: "aaron arney [alligator.io]"
// A slightly shorter way
unformattedName.replace(/([a-z]+)\.([a-z]+):([a-z]+\.[a-z]{2,10})/ig, '$1 $2 [$3]');
// expected output: "aaron arney [alligator.io]"
上記のスニペットでは、 $1
, $2
, $3
によって解釈される特別なパターンです replace
方法。
$1
–The first result from the match array
=>A reference to the first parenthesized group
$2
–The second result from the match array
=>A reference to the second parenthesized group
$n
-などなど
単語を大文字にするために、別の正規表現を使用できます。 上記のように出力をフォーマットする代わりに、関数を渡します。 この関数は、指定された引数を大文字にして返します。
ここでは、いくつかの新しいパーツを紹介します。 anchors
, alternation
、および新しい文字クラス [^]
.
[^abc]
– いいえa
,b
、 またc
\b
-単語の境界ab|cd
-論理「OR」、一致ab
またcd
// Capitalize the words
"aaron arney [alligator.io]".replace(/(^\b[a-z])|([^\.]\b[a-z])/g, (char) => char.toUpperCase());
// expected output: "Aaron Arney [Alligator.io]"
この表現を2つの部分に分解すると…
(^\b[a-z])
-文字列の最初の文字をキャプチャします。^
文字列の先頭に一致すると言います。|([^\.]\b[a-z])
-または、が終止符で始まらない新しい単語に一致します.
、これはTLDであるため。
探索を続ける
これは、正規表現の力のほんの少しの味です。 私が試した例は改善可能ですが、どうやって?
- 表現が冗長すぎませんか? 単純化しすぎていませんか?
- エッジケースをカバーしていますか?
- ネイティブメソッドを使用した巧妙な文字列操作に置き換えることができますか?
ここで、学んだ知識を取り入れて、それらの質問に答えようとします。 次のリソースを調べて、旅と実験に役立ててください。