4

文字列からデータを取り出す必要があります。残念ながら、データは本当に不親切な形式です。別の preg_replace に配置された約 15 の正規表現を作成する必要がありました。それ自体に多くの OR (|) があることは言うまでもありません。私の質問は、最終的に何をすべきかです。すべての式を 1 つに結合し、| を使用してそれらを分離します。またはそのままにしておきます - 個々の preg_replace?

明確さを保つために他の表現を作成することは非常に悪い習慣ですか? いくつかの表現を 1 つの表現に組み合わせることができると思いますが、非常に複雑になり、理解できません。

たとえば、私は持っています:

$itemFullName = preg_replace("@^\b(([a-zA-Z]{1,3})?[0-9]{1,2}(\.|\-|X)[0-9]{1,2}(\s|\.|\-)?(X|x)?\s?[0-9]{1,3}\.?(([0-9]{1,3})?(X[0-9]{1,3})|(\s[0-9]\/[0-9]|\/[0-9]{1,3}))?(\s\#[0-9]{1,3}\/[0-9]{1,3})?)\s@", ' ', $itemFullName, -1, $sum);
4

3 に答える 3

21

乱雑:

手始めに、元の PHP ステートメント:

$itemFullName = preg_replace("@^\b(([a-zA-Z]{1,3})?[0-9]{1,2}(\.|\-|X)[0-9]{1,2}(\s|\.|\-)?(X|x)?\s?[0-9]{1,3}\.?(([0-9]{1,3})?(X[0-9]{1,3})|(\s[0-9]\/[0-9]|\/[0-9]{1,3}))?(\s\#[0-9]{1,3}\/[0-9]{1,3})?)\s@", ' ', $itemFullName, -1, $sum);

次のようなコメントを付けてフリースペース モードで記述すると、はるかに読みやすく (そして保守しやすく) なります

几帳面:

$itemFullName = preg_replace("/(?#!php re_item_tidy Rev:20180207_0700)
    ^                     # Anchor to start of string.
    \b                    # String must begin with a word char.
    (                     # $1: Unnecessary group.
      ([a-zA-Z]{1,3})?    # $2: Optional 1-3 alphas.
      [0-9]{1,2}          # 1-2 decimal digits.
      (\.|\-|X)           # $3: Either a dot, hyphen or X.
      [0-9]{1,2}          # One or two decimal digits.
      (\s|\.|\-)?         # $4: Optional whitespace, dot or hyphen.
      (X|x)?              # $5: Optional X or x.
      \s?[0-9]{1,3}\.?    # Optional whitespace, 1-3 digits, optional dot.
      (                   # $6: Optional ??? from 2 alternatives.
        ([0-9]{1,3})?     # Either a1of2 $7: Optional 1-3 digits.
        (X[0-9]{1,3})     # $8: X and 1-3 digits.
      | (                 # Or a2of2 $9: one ??? from 2 alternatives.
          \s[0-9]\/[0-9]  # Either a1of2.
        | \/[0-9]{1,3}    # Or a2of2.
        )                 # End $9: one ??? from 2 alternatives.
      )?                  # End $6: optional ??? from 2 alternatives.
      (                   # $10: Optional sequence.
        \s\#[0-9]{1,3}    # whitespace, hash, 1-3 digits.
        \/[0-9]{1,3}      # Forward slash, 1-3 digits.
      )?                  # End $10: Optional sequence
    )                     # End $1: Unnecessary group.
    \s                    # End with a single whitespace char.
    /x", ' ', $itemFullName, -1, $sum);

批評:

この正規表現は、パフォーマンス的には悪くありません。最初に文字列アンカーの開始があり、一致しない文字列に対してすぐに失敗するのに役立ちます。また、後戻りの問題もありません。ただし、いくつかの小さな改善点があります。

  • 代替には 3 つのグループがあり、各代替は 1 文字のみで構成されます。これらの各代替は、単純な文字クラスに置き換えることができます。
  • 10 個のキャプチャ グループがありますが、preg_replace はキャプチャされたデータをまったく使用しません。これらのキャプチャ グループは、非キャプチャに変更できます。
  • 簡単に削除できる不要なグループがいくつかあります。
  • グループ 2:([a-zA-Z]{1,3})?は、次のように簡単に記述できます[a-zA-Z]{0,3}。グループ 7 は同様の構造を持っています。
  • 先頭の\b単語境界は不要です。
  • PHP では、単一引用符で囲まれた文字列で正規表現パターンを囲むのが最善です。二重引用符で囲まれた文字列には、エスケープする必要がある多くのメタ文字があります。一重引用符文字列には、一重引用符とバックスラッシュの 2 つしかありません。
  • 不必要にエスケープされたスラッシュがいくつかあります。

$sumによって行われる置換の数をカウントするために変数を使用していることにも注意してくださいpreg_replace()。パターンの先頭に文字列アンカーの開始があるため、複数行修飾子^を指定していないため、置換は 1 つしかありません。'm'実際に複数の置換を実行したい (そしてそれらを で数えたい) と想定しているので、修飾子$sumを追加しました。'm'

これらの変更を組み込んだ改良版を次に示します。

整理整頓:

$itemFullName = preg_replace('%(?#!php/m re_item_tidier Rev:20180207_0700)
    ^                  # Anchor to start of string.
    [a-zA-Z]{0,3}      # Optional 1-3 alphas.
    [0-9]{1,2}         # 1-2 decimal digits.
    [.X-]              # Either a dot, hyphen or X.
    [0-9]{1,2}         # One or two decimal digits.
    [\s.-]?            # Optional whitespace, dot or hyphen.
    [Xx]?              # Optional X or x.
    \s?[0-9]{1,3}\.?   # Optional whitespace, 1-3 digits, optional dot.
    (?:                # Optional ??? from 2 alternatives.
      [0-9]{0,3}       # Either a1of2: Optional 1-3 digits
      X[0-9]{1,3}      # followed by X and 1-3 digits.
    | (?:              # Or a2of2: One ??? from 2 alternatives.
        \s[0-9]/[0-9]  # Either a1of2.
      | /[0-9]{1,3}    # Or a2of2.
      )                # End one ??? from 2 alternatives.
    )?                 # End optional ??? from 2 alternatives.
    (?:                # Optional sequence.
      \s\#[0-9]{1,3}   # whitespace, hash, 1-3 digits.
      /[0-9]{1,3}      # Forward slash, 1-3 digits.
    )?                 # End optional sequence
    \s                 # End with a single whitespace char.
    %xm', ' ', $itemFullName, -1, $sum);

ただし、パフォーマンスが向上したとしても、あまり見られないと思います。元の正規表現はかなり優れています。パフォーマンスの問題は、おそらくプログラムの他の側面から発生しています。

お役に立てれば。

編集 2018-02-07 :余分な二重引用符を削除し、正規表現のシバンを追加しました。

于 2013-09-08T19:17:30.347 に答える
5

私の質問は、最終的に何をすべきかです。すべての式を 1 つに結合し、| を使用してそれらを分離します。またはそのままにしておきます - 個々の preg_replace?

preg_replace()保守性、可読性、効率性が向上するため、正規表現を個別の呼び出しに保持します。

正規表現で多数の OR 演算子を使用すると、|特に大量のテキストの場合にパフォーマンスが低下します。正規表現エンジンは入力のすべての文字に適用する必要があり、OR 演算子の|リスト内のすべての選択肢を適用する必要があるためです。

于 2013-09-08T16:54:42.110 に答える
2

Please don't worry about "fastest" without having first done some sort of measurement that it matters. Unless your program is operating too slowly, and you've run a profiler like XDebug to determine that the regex matching is the bottleneck, then you're doing premature optimization.

Rather than worrying about fastest, think about which way is clearest.

于 2013-09-08T20:56:50.567 に答える