このサイトの詳細 |
はじめに |
正規表現クイックスタート |
正規表現チュートリアル |
置換文字列チュートリアル |
アプリケーションと言語 |
正規表現の例 |
正規表現リファレンス |
置換文字列リファレンス |
書籍レビュー |
印刷可能なPDF |
このサイトについて |
RSSフィードとブログ |
先読みと後読みは、まとめて「先後読み」と呼ばれ、このチュートリアルの前半で説明した行頭と行末、および単語の先頭と末尾のアンカーと同様に、ゼロ幅アサーションです。違いは、先後読みは実際に文字に一致させますが、一致を放棄し、結果(一致または不一致)のみを返すことです。そのため、「アサーション」と呼ばれます。文字列内の文字を消費するのではなく、マッチが可能かどうかをアサートするだけです。先後読みを使用すると、それなしでは作成できない、または非常に長くて面倒な正規表現を作成できます。
否定先読みは、あるものに別のものが続かないものに一致させたい場合に不可欠です。文字クラスを説明するとき、このチュートリアルでは、否定された文字クラスを使用してqの後にuが続かないものに一致させることができない理由を説明しました。否定先読みが解決策を提供しますq(?!u)。否定先読みの構造は、疑問符と感嘆符が続く左括弧のペアです。先読みの中には、単純な正規表現u.
があります。肯定先読みはまったく同じように機能します。q(?=u)は、uが後に続くqに一致しますが、uをマッチの一部にすることはありません。肯定先読みの構造は、疑問符と等号が続く左括弧のペアです。
先読みの中では、任意の正規表現を使用できます(ただし、後読みでは後述するようにできません)。先読みの中では、有効な正規表現をすべて使用できます。先読みの中にキャプチャグループが含まれている場合、それらのグループは通常どおりキャプチャし、それらへの後方参照は、先読みの外側でも正常に機能します。(唯一の例外はTclで、先読み内のすべてのグループを非キャプチャとして扱います。)先読み自体はキャプチャグループではありません。後方参照を番号付けするためのカウントには含まれません。先読み内の正規表現のマッチを保存する場合は、先読み内の正規表現の周りにキャプチャ括弧を配置する必要があります。たとえば、(?=(regex))。逆は機能しません。これは、キャプチャグループがマッチを保存するまでに、先読みがすでに正規表現のマッチを破棄しているためです。
まず、エンジンがq(?!u)を文字列Iraqに適用する方法を見てみましょう。正規表現の最初のトークンはリテラルqです。すでに知っているように、これによりエンジンは文字列内のqが一致するまで文字列を走査します。文字列内の位置は、文字列の後の空白になります。次のトークンは先読みです。エンジンは現在、先読み構造の中にいることに注意し、先読み内の正規表現のマッチングを開始します。したがって、次のトークンはuです。これは文字列後の空白には一致しません。エンジンは、先読み内の正規表現が失敗したことに注意します。先読みが否定的なため、これは、先読みが現在の位置で正常に一致したことを意味します。この時点で、正規表現全体が一致し、qが一致として返されます。
同じ正規表現をquit. qに適用してみましょう。qと一致します。次のトークンは、先読み内のuです。次の文字はuです。これらは一致します。エンジンは次の文字iに進みます。ただし、先読み内の正規表現は完了しました。エンジンは成功を記録し、正規表現のマッチを破棄します。これにより、エンジンは文字列内のu.
にステップバックします。先読みが否定的なため、先読み内で成功したマッチは、先読みが失敗する原因になります。この正規表現の他の順列がないため、エンジンは最初からやり直す必要があります。qは他にどこにも一致できないため、エンジンは失敗を報告します。
先読みの意味を理解するために、もう一度中を見てみましょう。を適用してみましょうq(?=u)iをquitに。先読みは肯定になり、別のトークンが後に続きます。再び、qに適用してみましょう。qとuに適用してみましょう。uが一致します。繰り返しますが、先読みからのマッチは破棄する必要があるため、エンジンは文字列内のiからuにステップバックします。先読みは成功したので、エンジンはiを続けます。しかし、iはuに一致できません。したがって、このマッチ試行は失敗します。文字列にqが他にないため、残りの試行もすべて失敗します。
正規表現q(?=u)iは、何にも一致できません。同じ位置でuとiを一致させようとします。もしuの直後にqがある場合、先読みは成功しますが、その後にiがuに一致しません。もしuの直後にq以外のものがある場合、先読みは失敗します。
後読みは同じ効果がありますが、逆方向に機能します。後読み内のテキストがそこで一致するかどうかを確認するために、正規表現エンジンに一時的に文字列を後ろにステップバックするよう指示します。(?<!a)bは、否定後読みを使用して、「a」が前に付いていない「b」に一致します。これはcabに一致しませんが、b(とbのみ)bedまたはdebt. (?<=a)b(肯定後読み)はb(とbのみ)cabに一致しますが、bedまたはdebt.
には一致しません。肯定後読みの構造は(?<=text)です。左括弧のペアに、疑問符、「より小さい」記号、等号が続きます。否定後読みは次のように記述します。(?<!text)、等号の代わりに感嘆符を使用します。
を適用してみましょう。(?<=a)bをthingamabobエンジンは、後読みと文字列の最初の文字から開始します。この場合、後読みはエンジンに1文字後ろにステップバックし、そこでaが一致するかどうかを確認するように指示します。エンジンはtの前に文字がないため、1文字戻ることはできません。したがって、後読みは失敗し、エンジンは次の文字であるhで再び開始します。(否定後読みはここで成功することに注意してください。)繰り返しますが、エンジンは一時的に1文字後ろにステップバックして、「a」がそこにあるかどうかを確認します。それはtを見つけるため、肯定後読みは再び失敗します。
後読みは、正規表現が文字列内のmに到達するまで失敗し続けます。エンジンは再び1文字後ろにステップバックし、aがそこにあることを確認します。肯定後読みは一致します。ゼロ幅であるため、文字列内の現在の位置はmのままです。次のトークンはbですが、ここでは一致できません。次の文字は、文字列内の2番目のaです。エンジンはステップバックし、mがa.
に一致しないことを確認します。次の文字は、文字列内の最初のbです。エンジンはステップバックし、aが後読みを満たしていることを確認します。bに適用してみましょう。bとなり、正規表現全体が正常に一致しました。1文字に一致します。文字列内の最初のbです。
良いニュースは、正規表現の先頭だけでなく、どこでも後読みを使用できることです。「s」で終わらない単語を見つけたい場合は、次を使用できます\b\w+(?<!s)\b。これは明らかに\b\w+[^s]\bと同じではありません。をJohn'sに適用すると、前者はJohnに一致し、後者はJohn'(アポストロフィを含む)に一致します。理由を理解するのはあなた次第です。(ヒント\bはアポストロフィとsの間で一致します)。後者は、「a」や「I」のような1文字の単語にも一致しません。後読みを使用しない正しい正規表現は\b\w*[^s\W]\b(プラスの代わりにスター、文字クラス内の\W)。個人的には、後読みの方が理解しやすいと思います。正しく機能する最後の正規表現には、二重否定(否定された文字クラスの\W)があります。二重否定は、人間にとって混乱しやすい傾向があります。ただし、正規表現エンジンにはそうではありません。(否定された文字クラスで否定された短縮形をエラーとして扱うTclはおそらく例外です。)
残念ながら、ほとんどの正規表現フレーバーでは、後読みの中で任意の正規表現を使用することはできません。これは、正規表現エンジンが正規表現を逆方向に適用できないためです。正規表現エンジンは、後読みをチェックする前に、何文字戻る必要があるかを把握する必要があります。後読みを評価する際、正規表現エンジンは後読み内部の正規表現の長さを決定し、対象文字列内でその文字数だけ戻り、通常の正規表現と同様に後読み内部の正規表現を左から右に適用します。
Perl、Python、Boostなどで使用されているものを含む多くの正規表現フレーバーでは、固定長の文字列のみが許可されています。リテラルテキスト、文字エスケープ、\X以外のUnicodeエスケープ、および文字クラスを使用できます。量指定子や後方参照は使用できません。選択は使用できますが、すべての選択肢が同じ長さの場合に限ります。これらのフレーバーは、後読みを評価する際、最初に後読みが必要とする文字数だけ対象文字列を戻り、次に後読み内部の正規表現を左から右に通常どおり試行します。
Perl 5.30 は実験的な機能として可変長の後読みをサポートしていますが、正しく動作しない場合が多々あります。そのため、実際には Perl 5.30 でも上記が当てはまります。
PCRE は、後読みに関して完全に Perl と互換性があるわけではありません。Perl では後読み内部の選択肢は同じ長さである必要がありますが、PCRE では可変長の選択肢が許可されています。PHP、Delphi、R、およびRuby も同様に許可しています。各選択肢は固定長である必要があります。各選択肢は、別々の固定長の後読みとして扱われます。
Java はさらに一歩進んで、有限の繰り返しを許可しています。疑問符や、max パラメーターを指定した中括弧を使用できます。Java は後読みの最小と最大の可能な長さを決定します。正規表現内の後読み(?<!ab{2,4}c{3,5}d)testには5つの可能な長さがあります。7文字から11文字の長さになります。Java(バージョン6以降)が後読みを照合しようとする際、最初に文字列内で最小の文字数(この例では7)だけ戻り、次に後読み内部の正規表現を通常どおり左から右に評価します。失敗した場合、Java はさらに1文字戻って再度試行します。後読みが引き続き失敗する場合は、後読みが一致するか、最大文字数(この例では11)まで戻るまで、Java は戻り続けます。この対象文字列を繰り返し戻る処理は、後読みの可能な長さの数が増加するとパフォーマンスを低下させます。この点に注意してください。後読み内部での無限量指定子の欠如を回避するために、任意に大きな最大繰り返し数を選択しないでください。Java 4および5には、選択や可変量指定子を使用した後読みが、一部の状況で成功すべき場合に失敗するバグがあります。これらのバグは Java 6 で修正されました。
Java 13 では、後読み内でアスタリスクとプラス、および上限のない中括弧を使用できます。しかし、Java 13 は、Java 6 で導入された後読みの照合という面倒な方法を依然として使用しています。また、Java 13 は、複数量指定子のうち1つが上限なしの場合、後読みを正しく処理しません。状況によっては、エラーが発生する可能性があります。また、状況によっては、不正確な一致が得られる可能性があります。したがって、正確性とパフォーマンスの両方のために、Java 6 から 13 では後読みで上限の低い量指定子のみを使用することをお勧めします。
無限繰り返しと後方参照を含む、後読み内で完全な正規表現を使用できる正規表現エンジンは、JGsoft エンジンと.NET RegEx クラスのみです。これらの正規表現エンジンは、後読み内部の正規表現を本当に逆方向に適用し、後読み内部の正規表現と対象文字列を右から左に進みます。後読みの可能な長さがどれだけ多くても、後読みを評価する必要があるのは1回だけです。
最後に、std::regex や Tcl のようなフレーバーは、先読みをサポートしているにもかかわらず、後読みをまったくサポートしていません。JavaScript は、その登場以来、長い間そのようになっていました。しかし、後読みは ECMAScript 2018 仕様の一部になりました。この執筆時点(2019年後半)では、Google の Chrome ブラウザが、後読みをサポートする唯一の一般的な JavaScript 実装です。したがって、クロスブラウザ互換性が重要な場合は、JavaScript で後読みを使用することはできません。
後読みがゼロ長の性質を持つという事実は、自動的にアトミックになります。後読みの条件が満たされるとすぐに、正規表現エンジンは後読み内部のすべてを忘れます。後読み内部でバックトラックして、異なる順列を試行することはありません。
これが違いを生む唯一の状況は、後読み内部でキャプチャグループを使用する場合です。正規表現エンジンは後読みにバックトラックしないため、キャプチャグループの異なる順列を試行することはありません。
このため、正規表現(?=(\d+))\w+\1は決して123x12には一致しません。最初に後読みが123をキャプチャし、次に\1. \w+が文字列全体に一致し、次に1だけに一致するまでバックトラックします。最後に、\w+は、\1がどの位置でも一致しないため失敗します。ここで、正規表現エンジンにはバックトラックするものがなくなり、正規表現全体が失敗します。の\d+によって作成されたバックトラックステップは破棄されています。先読みが12.
だけをキャプチャする時点には決して到達しません。明らかに、正規表現エンジンは文字列内のさらに先の位置を試行します。対象文字列を変更すると、正規表現(?=(\d+))\w+\1は56x56の456x56.
に一致します。後読み内でキャプチャグループを使用しない場合、これらすべては問題になりません。後読み条件が満たされるかどうかだけです。満たされる方法がいくつあるかは関係ありません。
| クイック スタート | チュートリアル | ツール & 言語 | 例 | リファレンス | 書籍 レビュー |
| はじめに | 目次 | 特殊文字 | 非印刷文字 | 正規表現エンジンの内部構造 | 文字クラス | 文字クラスの減算 | 文字クラスの交差 | 省略形文字クラス | ドット | アンカー | 単語境界 | 選択 | オプションの項目 | 繰り返し | グループ化とキャプチャ | 後方参照 | 後方参照、パート2 | 名前付きグループ | 相対後方参照 | ブランチリセットグループ | 自由な間隔とコメント | Unicode | モード修飾子 | アトミックグループ化 | 所有量指定子 | 先読みと後読み | 後読み、パート2 | 一致からテキストを除外する | 条件 | バランシンググループ | 再帰 | サブルーチン | 無限再帰 | 再帰と量指定子 | 再帰とキャプチャ | 再帰と後方参照 | 再帰とバックトラック | POSIXブラケット式 | ゼロ長の一致 | 継続的な一致 |
ページURL: https://regular-expressions.dokyumento.jp/lookaround.html
ページ最終更新日: 2021年8月12日
サイト最終更新日: 2024年3月15日
Copyright © 2003-2024 Jan Goyvaerts. All rights reserved.