2022年8月7日

オルタネーション(OR)|

オルタネーションとは、正規表現における単純な「OR」を意味する用語です。

正規表現では、縦線文字|で表されます。

例えば、プログラミング言語HTML、PHP、Java、またはJavaScriptを見つけ出す必要があるとします。

対応する正規表現はhtml|php|java(script)?です。

使用例

let regexp = /html|php|css|java(script)?/gi;

let str = "First HTML appeared, then CSS, then JavaScript";

alert( str.match(regexp) ); // 'HTML', 'CSS', 'JavaScript'

既に似たものを見てきました ― 角括弧です。これらは複数の文字の中から選択することを可能にし、例えばgr[ae]ygrayまたはgreyにマッチします。

角括弧は文字または文字クラスのみを許容します。オルタネーションは任意の式を許容します。A|B|Cという正規表現は、式AB、またはCのいずれかを意味します。

例えば

  • gr(a|e)ygr[ae]yと全く同じ意味です。
  • gra|eygraまたはeyを意味します。

パターンの一部のみにオルタネーションを適用するには、括弧で囲むことができます。

  • I love HTML|CSSI love HTMLまたはCSSにマッチします。
  • I love (HTML|CSS)I love HTMLまたはI love CSSにマッチします。

例:時刻の正規表現

以前の記事では、hh:mm形式の時刻(例えば12:00)を検索するための正規表現を作成するタスクがありました。しかし、単純な\d\d:\d\dは曖昧すぎます。これは25:99を時刻として受け入れてしまいます(99分はパターンにマッチしますが、その時刻は無効です)。

より良いパターンをどのように作成できるでしょうか?

より注意深いマッチングを使用できます。まず、時間です。

  • 最初の桁が0または1の場合、次の桁は何でもかまいません。[01]\d
  • そうでない場合、最初の桁が2の場合、次の桁は[0-3]でなければなりません。
  • (他の最初の桁は許可されません)

オルタネーションを使用して正規表現の両方のバリアントを記述できます。[01]\d|2[0-3]

次に、分は00から59でなければなりません。正規表現言語では、[0-5]\dとして記述できます。最初の桁は0-5、その後は任意の桁です。

時間と分を組み合わせると、パターンは[01]\d|2[0-3]:[0-5]\dになります。

ほぼ完成していますが、問題があります。|のオルタネーションが[01]\d2[0-3]:[0-5]\dの間にあります。

つまり、分が2番目のオルタネーションバリアントに追加されています。これが明確な図です。

[01]\d  |  2[0-3]:[0-5]\d

そのパターンは[01]\dまたは2[0-3]:[0-5]\dを探します。

しかし、これは間違っています。オルタネーションは正規表現の「時間」部分でのみ使用して、[01]\dまたは2[0-3]を許可する必要があります。「時間」を括弧で囲むことで修正しましょう。([01]\d|2[0-3]):[0-5]\d

最終的な解決策

let regexp = /([01]\d|2[0-3]):[0-5]\d/g;

alert("00:00 10:10 23:59 25:99 1:2".match(regexp)); // 00:00,10:10,23:59

課題

Java、JavaScript、PHP、C、C++など、多くのプログラミング言語があります。

文字列Java JavaScript PHP C++ Cの中でそれらを見つける正規表現を作成してください。

let regexp = /your regexp/g;

alert("Java JavaScript PHP C++ C".match(regexp)); // Java JavaScript PHP C++ C

最初のアイデアは、言語を|で区切って列挙することです。

しかし、それは正しく機能しません。

let regexp = /Java|JavaScript|PHP|C|C\+\+/g;

let str = "Java, JavaScript, PHP, C, C++";

alert( str.match(regexp) ); // Java,Java,PHP,C,C

正規表現エンジンは、オルタネーションを1つずつ順番に探します。つまり、まずJavaがあるかどうかを確認し、そうでない場合はJavaScriptを探します。

その結果、Javaが最初にチェックされるため、JavaScriptは見つかりません。

CC++についても同様です。

この問題には2つの解決策があります。

  1. より長いマッチを最初にチェックするように順序を変更する:JavaScript|Java|C\+\+|C|PHP
  2. 同じ先頭を持つバリアントをマージする:Java(Script)?|C(\+\+)?|PHP

動作中

let regexp = /Java(Script)?|C(\+\+)?|PHP/g;

let str = "Java, JavaScript, PHP, C, C++";

alert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++

「bbタグ」は[tag]...[/tag]のように見え、tagburl、またはquoteのいずれかです。

例えば

[b]text[/b]
[url]http://google.com[/url]

BBタグはネストできます。ただし、タグは自分自身の中にネストすることはできません。例えば

Normal:
[url] [b]http://google.com[/b] [/url]
[quote] [b]text[/b] [/quote]

Can't happen:
[b][b]text[/b][/b]

タグには改行を含めることができます。それは普通です。

[quote]
  [b]text[/b]
[/quote]

その内容を含むすべてのBBタグを見つける正規表現を作成してください。

例えば

let regexp = /your regexp/flags;

let str = "..[url]http://google.com[/url]..";
alert( str.match(regexp) ); // [url]http://google.com[/url]

タグがネストされている場合、外部タグが必要です(必要であれば、その内容の検索を続けることができます)。

let regexp = /your regexp/flags;

let str = "..[url][b]http://google.com[/b][/url]..";
alert( str.match(regexp) ); // [url][b]http://google.com[/b][/url]

開始タグは\[(b|url|quote)]です。

次に、閉じタグまでのすべてを見つけるには、改行を含む任意の文字にマッチするフラグs付きのパターン.*?を使用し、閉じタグへの後方参照を追加します。

完全なパターン:\[(b|url|quote)\].*?\[/\1]

動作中

let regexp = /\[(b|url|quote)].*?\[\/\1]/gs;

let str = `
  [b]hello![/b]
  [quote]
    [url]http://google.com[/url]
  [/quote]
`;

alert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote]

[のエスケープに加えて、閉じタグ[\/\1]のスラッシュをエスケープする必要がありました。通常、スラッシュはパターンを閉じます。

二重引用符"..."で囲まれた文字列を見つける正規表現を作成してください。

文字列は、JavaScript文字列と同じ方法でエスケープをサポートする必要があります。例えば、引用符は\"、改行は\n、バックスラッシュ自体は\\として挿入できます。

let str = "Just like \"here\".";

特に、エスケープされた引用符\"は文字列を終了しません。

そのため、途中のエスケープされた引用符を無視して、ある引用符から別の引用符まで検索する必要があります。

これは、タスクの重要な部分です。そうでなければ、それは些細なことでしょう。

マッチする文字列の例

.. "test me" ..
.. "Say \"Hello\"!" ... (escaped quotes inside)
.. "\\" ..  (double backslash inside)
.. "\\ \"" ..  (double backslash and an escaped quote inside)

JavaScriptでは、バックスラッシュを2倍にする必要があります。

let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';

// the in-memory string
alert(str); //  .. "test me" .. "Say \"Hello\"!" .. "\\ \"" ..

解答:/"(\\.|[^"\\])*"/g

ステップバイステップ

  • 最初に、開始引用符"を探します。
  • 次に、バックスラッシュ\\がある場合(パターンでは特殊文字であるため2倍にする必要があります)、その後には任意の文字が許容されます(ドット)。
  • そうでない場合、引用符(文字列の終わりを意味します)とバックスラッシュ(孤立したバックスラッシュを防ぐため、バックスラッシュはその後ろに別の記号がある場合のみ使用されます)以外の任意の文字を取ります。[^"\\]
  • …そして、閉じ引用符まで続きます。

動作中

let regexp = /"(\\.|[^"\\])*"/g;
let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';

alert( str.match(regexp) ); // "test me","Say \"Hello\"!","\\ \""

タグ<style...>を見つける正規表現を作成してください。属性がない場合<style>、または複数の属性がある場合<style type="..." id="...">、完全なタグにマッチする必要があります。

…しかし、正規表現は<styler>にマッチしてはなりません!

例えば

let regexp = /your regexp/g;

alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">

パターンの開始は明らかです:<style

…しかし、<styler>がマッチするため、単純に<style.*?>と書くことはできません。

<styleの後にスペースがあり、オプションで他の何かがあるか、または終了の>が必要です。

正規表現言語では:<style(>|\s.*?>)

動作中

let regexp = /<style(>|\s.*?>)/g;

alert( '<style> <styler> <style test="...">'.match(regexp) ); // <style>, <style test="...">
チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善点の提案がある場合は、コメントする代わりにGitHub issueまたはプルリクエストを送信してください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • コードを数行挿入するには<code>タグを使用し、複数行の場合は<pre>タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください。