web-dev-qa-db-ja.com

正規表現での先読みアサーションの使用

私の毎日の仕事はPerlで90%であるため、私は日常的に正規表現を使用しています(レガシーコードベースですが、それは別の問題です)。それにもかかわらず、私はまだ先読みと後読みがひどく混乱していて、しばしば読めないことに気づきます。今のところ、先読みまたは後読みでコードレビューを取得する場合は、すぐにそれを送り返して、複数の正規表現または別のアプローチを使用して問題を解決できるかどうかを確認します。以下は、私がそれらを好きにしない傾向がある主な理由です。

  • それらはひどく読めない可能性があります。たとえば、先読みアサーションは、配置されている場所に関係なく、文字列の先頭から始まります。それは 他のものの中で であり、非常に「興味深く」、明白でない振る舞いを引き起こす可能性があります。
  • 以前は、多くの言語が先読み/後読みをサポートしていない(または「実験的機能」としてサポートしていない)場合がありました。これはそれほどのケースではありませんが、それがどの程度サポートされているかについては常に疑問があります。
  • 率直に言って、彼らは汚いハックのように感じます。多くの場合、正規表現はすでに存在しますが、非常にエレガントな場合もあり、広く受け入れられています。
  • 私はそれらを全く必要とせずに通り抜けました...時々私はそれらが無関係であると思います。

さて、特に最後の二つの理由は本当にいいものではないことは認めますが、一つ見たときに頭のなかで何が起きているのかを列挙しておかなければならないと感じました。私はそれらについての考えを変えたいと思っていますが、それらは次のようなプログラミングの私のコア原則のいくつかに違反していると感じています。

  • コードは、機能を犠牲にすることなく、できるだけ読みやすくする必要があります。これには、効率が低くても、アプリケーション全体としての違いが無視できるか重要でない限り、より明確な方法で何かを行うことが含まれます。
  • コードは保守可能である必要があります-別のプログラマーが私のコードを修正するためにやって来た場合、非自明な振る舞いはバグを隠したり、関数型コードをバグのように見せたりする可能性があります(読みやすさを参照)
  • 「適切な仕事に適したツール」-先読みを使用できるような工夫された例を思い付くことができると思いますが、実際の開発作業でそれらを本当に必要とするものに出くわしたことはありません。たとえば、複数の正規表現とは対照的に、それらが本当に最適なツールであるものはありますか(あるいは、今日使用されているほとんどの場合に最適なツールですか)?

私の質問はこれです:正規表現で先読み/後読みを使用するのは良い習慣ですか、それとも現代のプロダクションコードに侵入した単なるハックですか?

私はこれについて間違っていると確信できて完全にうれしいです。簡単な例は例や説明に役立ちますが、それだけでは私を納得させるのに十分ではありません。

5
Greg Jackson

私はまだ先読みと後読みがひどく混乱していて、しばしば判読できないことに気づきます。

正規表現を分解してコメントできることをご存知ですか?

$foo =~ m/^
  (?=.*a)           # must contain an a somewhere
  (?=.*c)           # must contain a c somewhere
  (?=.*1)           # must contain a 1 somewhere
  (?=.*2)           # must contain a 2 somewhere
  \S+               # all non-space characters
$/x

正規表現で先読み/後読みを使用することは良い習慣ですか、それとも単に最新の製品コードへの道を見つけたハックですか?

壊滅的なバックトラッキング および 正規表現関連のセキュリティ問題 を回避するために、これらは非常に不可欠です。理想的にはプレーン アトミックグループ も使用します。

ナイーブな同等物と比較して、上記の式がどのようにバックトラックするかを比較します。

$foo =~ m/^
  \S*a\S*c\S*1\S*2\S*      # a, then c, then 1, then 2
 |
  \S*a\S*c\S*2\S*1\S*      # a, c, 2, 1
 |
  \S*a\S*1\S*c\S*2\S*      # a, 1, c, 2
 |
  \S*a\S*1\S*2\S*c\S*      # a, 1, 2, c
 |
  # ... etc
$/x

特に、長い入力とa、c、2(1なし)のランダムシーケンスを使用します。

6

非常に一般的な見方をするためには、お互いのトレードオフを比較検討する必要があります。一方では、いくつかの実装でのみサポートされている高度な機能があります。これは、非常に熟練していないと読みにくいものです。もう1つは、より単純な構成を使用した(非常に長いと思われる)コードの断片です。どちらが最適かは、アクセシビリティ(元のプログラマーとあなた自身のの習熟度)およびaccuracyspeedなどのより抽象的な懸念事項-)。原則として、私自身の意見(約10年間正規表現を使用した後):

  • コードが使い捨てでない限り(そして、誰もが知っている それがどのように終了するか である場合)、正規表現を慎重に使用します。それらは問題の8次元モデルをまとめるのに少し似ているため、それらを解くのは困難です。
  • 短い正規表現を短い処理時間と同じにしないでください。 5行substr/if-else構築は非常に高速である可能性があるため、アプローチを実行する前に試してください。
  • コーナーケースの処理が非常に困難になることがよくあります(HTMLを解析するための正規表現について尋ねるすべての人を目撃してください)。ストリングの正規表現スタイル全体を食べるのではなく、分割統治します。
1
l0b0