PHP 正则表达式性能

标签 php regex preg-replace

我必须从字符串中取出一些数据。不幸的是,数据的格式非常不友好。我必须创建大约 15 个正则表达式放在单独的 preg_replace 中。值得一提的是,它们本身就有很多或(|)。我的问题是我最终应该做什么:将所有表达式合并为一个并使用 | 将它们分开或者让它们保持原样 - 在单独的 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);

最佳答案

不整洁:

对于初学者,您的原始 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);

批评:

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

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

另请注意,您正在使用 $sum 变量来计算 preg_replace() 进行的替换次数。由于您在模式的开头有一个 ^ 开始的字符串 anchor ,您将只有一个替换,因为您没有指定 '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:删除了多余的双引号,添加了正则表达式 shebang。

关于PHP 正则表达式性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18686168/

相关文章:

php - Google API Oauth php 永久访问

php - 将每个 PHP 中的数字相加?

Java - 模式匹配

php - 使用 preg_replace 替换单引号之间的所有点字符

php - 目标类 [App\Http\Controllers\Api\Auth\AuthController] 不存在

c# - 如何编写我自己的正则表达式

php - 正则表达式仅在 { } 内替换

php - regex/preg_replace 替换子域

php - 数字或特定数字的正则表达式

PHP、正则表达式 - 如何使用 preg_replace 转义\n?