クイックスタート
チュートリアル
ツールと言語
リファレンス
書籍レビュー
正規表現チュートリアル
はじめに
目次
特殊文字
印字不能文字
正規表現エンジンの内部
文字クラス
文字クラスの減算
文字クラスの積集合
短縮文字クラス
ドット
アンカー
単語境界
選択
オプション項目
繰り返し
グループ化とキャプチャ
後方参照
後方参照 パート2
名前付きグループ
相対後方参照
分岐リセットグループ
フリースペーシングとコメント
Unicode
モード修飾子
アトミックグループ
所有量指定子
先読みと後読み
先読みと後読み パート2
テキストをマッチから除外する
条件式
バランスグループ
再帰
サブルーチン
無限再帰
再帰と量指定子
再帰とキャプチャ
再帰と後方参照
再帰とバックトラッキング
POSIXブラケット式
長さゼロのマッチ
マッチの継続
このサイトの詳細
はじめに
正規表現クイックスタート
正規表現チュートリアル
置換文字列チュートリアル
アプリケーションと言語
正規表現の例
正規表現リファレンス
置換文字列リファレンス
書籍レビュー
印刷可能なPDF
このサイトについて
RSSフィードとブログ
RegexBuddy—Better than a regular expression tutorial!

長さゼロの正規表現マッチ

アンカー単語境界先読み/後読みは、文字ではなく位置にマッチすることを説明しました。つまり、正規表現がアンカー、単語境界、または先読み/後読みのみで構成されている場合、長さゼロのマッチが発生する可能性があります。状況によっては、これは非常に便利であったり、望ましくない場合もあります。

たとえば、メールでは、引用符で囲まれたメッセージの各行の先頭に「大なり記号」とスペースを追加するのが一般的です。VB.NETでは、次のように簡単に実行できます。Dim Quoted As String = Regex.Replace(Original, "^", "> ", RegexOptions.Multiline)複数行モードを使用しているため、正規表現^は、引用符で囲まれたメッセージの先頭と各改行の後でマッチします。 Regex.Replaceメソッドは、文字列から正規表現のマッチを削除し、置換文字列(大なり記号とスペース)を挿入します。マッチには文字が含まれていないため、何も削除されません。ただし、マッチには開始位置が含まれています。置換文字列は、そこに挿入されます。

を使用すると^\d*$ユーザーが数値を入力したかどうかをテストすると、望ましくない結果が生じます。スクリプトが空の文字列を有効な入力として受け入れる原因となります。その理由を見てみましょう。

空の文字列には、「文字」位置は1つだけです。文字列の後の空虚です。正規表現の最初のトークンは^です。文字列の前の空虚が前にあるため、文字列の後の空虚の前の位置にマッチします。次のトークンは\d*です。アスタリスクの効果の1つは、\dをオプションにすることです。エンジンは、\dを文字列の後の空虚とマッチさせようとします。これは失敗します。しかし、アスタリスクは\dの失敗を長さゼロの成功に変えます。エンジンは、文字列内の位置を進めることなく、次の正規表現トークンに進みます。そのため、エンジンは$と文字列の後の空虚に到達します。これらはマッチします。この時点で、正規表現全体が空の文字列にマッチし、エンジンは成功を報告します。

解決策は、正規表現^\d+$を適切な量指定子とともに使用して、少なくとも1桁を入力する必要があるようにすることです。各行の先頭または末尾にマッチするなどの特殊な場合を除いて、正規表現が長さゼロのマッチを見つけられないように常に確認すれば、このトピックの残りの部分を読んで得られる頭痛の種を回避できます。

長さゼロのマッチをスキップする

すべての実装で長さゼロのマッチがサポートされているわけではありません。Delphi XE5以前のTRegExクラスは、常に長さゼロのマッチをスキップします。TPerlRegExクラスもXE5以前ではデフォルトでスキップしますが、Stateプロパティを介してこれを変更できます。Delphi XE6以降では、TRegExは長さゼロのマッチをスキップしませんが、TPerlRegExはデフォルトではスキップしませんが、Stateプロパティを介してスキップできます。PCREはデフォルトで長さゼロのマッチを見つけますが、PCRE_NOTEMPTYを設定するとスキップできます。

長さゼロの正規表現マッチの後に進む

正規表現が文字列内の任意の位置で長さゼロのマッチを見つけることができる場合、それは見つかります。正規表現\d*は、ゼロ個以上の数字にマッチします。対象の文字列に数字が含まれていない場合、この正規表現は文字列内のすべての位置で長さゼロのマッチを見つけます。文字列abcでは、4つのマッチが見つかります。3つの文字のそれぞれの前と文字列の末尾に1つずつです。

正規表現が任意の位置で長さゼロのマッチと特定の長さゼロ以外のマッチを見つけることができる場合、事態は複雑になります。正規表現\d*|x、対象の文字列x1、および正規表現エンジンが長さゼロのマッチを許可するとします。すべてのマッチを反復処理すると、どのマッチがいくつ得られますか?答えは、正規表現エンジンが長さゼロのマッチの後にどのように進むかによって異なります。どちらにしても答えは複雑です。

最初のマッチ試行は、文字列の先頭から始まります。\dxとマッチしません。しかし、*\dをオプションにします。最初の選択肢は、文字列の先頭で長さゼロのマッチを見つけます。ここまで、長さゼロのマッチを許可するすべての正規表現エンジンは同じように動作します。

ここで、正規表現エンジンは難しい状況にあります。文字列全体を調べて、重複しないすべての正規表現マッチを見つけるように求めています。最初のマッチは、最初のマッチ試行が開始された文字列の先頭で終了しました。正規表現エンジンは、文字列の先頭で同じ長さゼロのマッチを永遠に見つける無限ループに陥るのを回避する方法が必要です。

ほとんどの正規表現エンジンで使用されている最も簡単な解決策は、前のマッチが長さゼロだった場合、前のマッチの終了後1文字から次のマッチ試行を開始することです。この場合、2番目のマッチ試行は、文字列内のx1の間の位置から始まります。\d1とマッチします。文字列の末尾に到達しました。量指定子*は、1回の繰り返しで満たされます。1が全体的なマッチとして返されます.

Perlで使用されているもう1つの解決策は、長さゼロかどうかに関係なく、常に前のマッチの終わりに次のマッチ試行を開始することです。長さゼロだった場合、エンジンはその位置で長さゼロのマッチを許可してはならないため、それを記録します。したがって、Perlは2番目のマッチ試行も文字列の先頭から開始します。最初の選択肢は、再び長さゼロのマッチを見つけます。しかし、これは有効なマッチではないため、エンジンは正規表現をバックトラックします. \d*は、長さゼロのマッチをあきらめることを余儀なくされます。ここで、正規表現の2番目の選択肢が試行されます. xxとなり、2番目のマッチが見つかります。3番目のマッチ試行は、文字列内のxの後の位置から始まります。最初の選択肢は1とマッチし、3番目のマッチが見つかります。

しかし、正規表現エンジンはまだ完了していません。xがマッチした後、文字列の末尾から始まるマッチ試行をもう1回行います。ここでも\d*は長さゼロのマッチを見つけます。したがって、エンジンが長さゼロのマッチの後にどのように進むかによって、3つまたは4つのマッチが見つかります。

1つの例外は、JGsoftエンジンです。JGsoftエンジンは、ほとんどのエンジンと同様に、長さゼロのマッチの後に1文字進みます。ただし、前のマッチが終了した位置で長さゼロのマッチをスキップするという追加のルールがあるため、長さゼロ以外のマッチのすぐ隣に長さゼロのマッチを配置することはできません。この例では、JGsoftエンジンは2つのマッチのみを見つけます。文字列の先頭の長さゼロのマッチと1.

Python 3.6以前は、長さゼロのマッチの後に進みます。gsub()検索と置換関数は、前の長さゼロ以外のマッチが終了した位置で長さゼロのマッチをスキップしますが、finditer()関数はそれらのマッチを返します。そのため、Pythonでの検索と置換はJust Great Softwareアプリケーションと同じ結果になりますが、すべてのマッチをリストすると、文字列の末尾に長さゼロのマッチが追加されます.

Python 3.7はこれらすべてを変更しました。Perlのように長さゼロのマッチを処理します。gsub()は、別のマッチに隣接する長さゼロのマッチを置き換えるようになりました。これは、長さゼロのマッチを見つけることができる正規表現は、Python 3.7と以前のバージョンのPythonの間で互換性がないことを意味します.

PCRE 8.00以降とPCRE2は、バックトラッキングによってPerlのように長さゼロのマッチを処理します。PCRE 7.9のように、長さゼロのマッチの後に1文字進むことはなくなりました。

RPHPのregexp関数はPCREに基づいているため、PCREのようにバックトラッキングによって長さゼロのマッチでスタックするのを回避します。しかし、Rのgsub()検索と置換関数は、gsub()と同様に、前の長さゼロ以外のマッチが終了した位置で長さゼロのマッチもスキップします。Python 3.6以前のように。Rの他のregexp関数とPHPのすべての関数は、PCRE自体と同様に、長さゼロ以外のマッチのすぐ隣に長さゼロのマッチを許可します。

プログラマーのための注意事項

などの正規表現は、それ自体で文字列の末尾に長さゼロのマッチを見つけることができます。エンジンの文字位置を照会すると、文字列インデックスがゼロベースの場合は文字列の長さ、プログラミング言語で文字列インデックスが1ベースの場合は長さ+1が返されます。マッチの長さをエンジンの照会すると、ゼロが返されます. $

注意が必要なのは、String [Regex.MatchPosition]がアクセス違反またはセグメンテーション違反を引き起こす可能性があることです。これは、MatchPositionが文字列の後の空虚を指している可能性があるためです。これは、複数行モードで文字列の最後の文字が改行の場合にも発生する可能性があります。^^$