2012-02-21 4 views
0

私はいくつかの短いフレーズをマッチさせたいと思います。正規表現とPythonを使ったフレーズマッチング

(^|)(piston|piston ring)(|$) 

を「ピストン」に上記regex.match("piston ring")マッチを使用して次のように私は、正規表現を使用します。長いフレーズ「ピストンリング」が最初に来るように正規表現を変更すると、期待どおりに動作します。

正規表現の貪欲な性質が "無料"の最長文字列と一致しようとしていると仮定しているので、私はこの動作に驚いていました。

私には何が欠けていますか?誰かがこれを説明できますか?ありがとう!

+1

正規表現の貪欲は '*'と '+ '演算子を使っているときにのみ有効です。 '|'は左から右へ最初のマッチを使います。 – resmon6

答えて

5

だと思います。したがって、あなたの例ではpistonで一致することができますので、piston ringは試行されません。

この正規表現を書くためのより良い方法は、このようなものになるだろう:

(^|)(piston(ring)?)(|$) 

これは'piston'に一致するようにしようとし、その後、すぐにそれをオプションに?で、' ring'にマッチしようとします。あるいは、より長い選択肢が交互の開始時に出現するようにしてください。

(^|)(|$)の代わりにword boundary,\bを使用することをお勧めします。 http://www.regular-expressions.info/alternation.html(最初のGoogle結果)から

+0

あなたの代わりの解決策のために+1 – stema

+0

良い結果を得るためにリストを長さの逆順にソートしました。私はあなたの助言を受けて、分かりやすくするために\ bを使いました。助けてくれてありがとう! – ccgillett

2

これはAlternationsの動作です。それは成功した場合は、 "ピストン"である最初の選択肢にマッチしようとします。

これはすべての選択肢を試していないことを意味します。つまり、最初に一致するもので終了します。

あなたは単語の境界\bあるためにも、面白いかもしれ何regular-expressions.info

に、ここで詳細を見つけることができます。私は、あなたが探していることは、正規表現で交代(|)を使用する場合、一致を見つけることができるようになるまで、左から右へ、各オプションを順番に試みられ

\bpiston(?: ring)?\b 
4

正規表現エンジンが熱望しています。有効な一致が見つかるとすぐに検索を停止します。その結果は、特定の状況では、選択肢の順番が重要ということです

一つの例外:

最長マッチが、返される正規表現エンジンはNFAを使用して実装されているかどうかに関係なくPOSIX標準の義務付けまたはDFAアルゴリズム。

可能な解決策:

  • piston(ring)?
  • (piston ring|piston)

      は(前に最長入れる)
  • +1

    これは繰り返し表現が貪欲である理由を理解するために読むのも良いことです。 http://www.regular-expressions.info/repeat.html – resmon6

    0
    Edit2: It wasn't clear if your test data 
    contained pipes or not. I saw the pipes in 
    the regex and assumed you are searching 
    for pipe delim. Oh well.. not sure if below 
    helps. 
    

    ピックアップしてより多くの交替が必要になりますパイプ区切りのテキストを一致させるために正規表現を使用しました開始列と終了列。

    別のアプローチはどうですか?

    text='start piston|xxx|piston ring|xxx|piston cast|xxx|piston|xxx|stock piston|piston end' 
    j=re.split(r'\|',text) 
    
    k = [ x for x in j if x.find('piston') >= 0 ] 
    ['start piston', 'piston ring', 'piston cast', 'piston', 'stock piston', 'piston end'] 
    
    k = [ x for x in j if x.startswith('piston') ] 
    ['piston ring', 'piston cast', 'piston', 'piston end'] 
    
    k = [ x for x in j if x == 'piston' ] 
    ['piston'] 
    
    j=re.split(r'\|',text) 
    if 'piston ring' in j: 
        print True 
    > True 
    

    編集:明確にする - この例を取る: ''

    =テキスト2 'piston1 | XXX | spiston2 | XXX |ピストンリング| | XXX piston3'

    私が追加一致するアイテムを表示するために一致するもの

    re.findall('piston.',text2) 
    ['piston1', 'piston2', 'piston ', 'piston3'] 
    

    もっと正確にするためには、ルックアヘッドアサーションを使用する必要があります。 これは、あなたが一致保証「|ピストン」は貪欲から最初に一致した文字に一致する結果

    re.findall('(?<=\|)piston.',text2) 
    ['piston ', 'piston3'] 
    

    リミットにパイプが含まれていませんか*。? <ストップ・キャラクタ> パイプを除外するためにグループ化の括弧を追加してください。試合 。*?グループ内であるかどうかを検出するのに十分にスマートで、括弧を無視し、次の文字を停止マッチングのセンチネルとして使用します。これはうまくいくようですが、最後の列は無視されます。

    re.findall('(?<=\|)(piston.*?)\|',text2) 
    ['piston ring'] 
    

    あなたはあなたが今だけ指定することができ、グループ化の追加だけでなく、最後の列を検索し、この非グループ化一致を追加するには、エスケープパイプ

    re.findall('\|(piston.*?)\|',text2) 
    ['piston ring'] 
    

    で始まる(?:\ || $) - パイプ上でマッチすること(エスケープする必要がある)か、文字列の最後($)であることを意味します。 グループ化されていない一致(?:x1 | x2)は結果に含まれません。追加ボーナスは最適化されます。

    re.findall('\|(piston.*?)(?:\||$)',text2) 
    ['piston ring', 'piston3'] 
    

    最後に、最後の文字列一致

    re.findall('(?:\||^)(piston.*?)(?:\||$)',text2) 
    ['piston1', 'piston ring', 'piston3'] 
    

    のためにずっと以前のもののような他の変更を追加し、文字列の先頭のために固定して、それがお役に立てば幸いです。 :)