4

我必须从字符串中取出一些数据。不幸的是,数据的格式非常不友好。我必须创建大约 15 个正则表达式,放在单独的 preg_replace 中。值得一提的是,它们内部有许多 OR (|)。我的问题是我最后应该做什么:将所有表达式组合成一个并使用 | 将它们分开 或者让它们保持原样 - 在单独的 preg_replace 中?

创建其他表达式以保持清晰度是非常糟糕的做法吗?我想也许我可以将一些表达组合成一个,但它们变得非常复杂并且无法理解。

例如我有:

$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);

批判:

这个正则表达式在性能方面确实不错。它在开头有一个字符串锚点的开头,这有助于它在不匹配的字符串中快速失败。它也没有任何回溯问题。但是,可以进行一些小的改进:

  • 有三组备选方案,其中每个备选方案仅由一个字符组成——每一个都可以用一个简单的字符类替换。
  • 有 10 个捕获组,但 preg_replace 不使用任何捕获的数据。这些捕获组可以更改为非捕获。
  • 有几个不必要的组可以简单地删除。
  • 第 2 组:([a-zA-Z]{1,3})?可以更简单地写为:[a-zA-Z]{0,3}. 第 7 组有类似的结构。
  • 开头的\b单词边界是不必要的。
  • 使用 PHP,最好将正则表达式模式包含在单引号字符串中。双引号字符串有许多必须转义的元字符。单引号字符串只有两个:单引号和反斜杠。
  • 有一些不必要的转义正斜杠。

另请注意,您正在使用该$sum变量来计算preg_replace(). 由于您^在模式的开头有一个字符串锚点的开头,因此您只会有一个替换,因为您没有指定'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:删除无关的双引号,添加正则表达式 shebangs。

于 2013-09-08T19:17:30.347 回答
5

我的问题是我最后应该做什么:将所有表达式组合成一个并使用 | 将它们分开 或者让它们保持原样 - 在单独的 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 回答