3

我需要在 PHP 中针对给定字符串中的许多文章引用进行模式匹配,然后我想提取它们。引用最多可以有 16 位数字,每个数字都可以有不同的值。要知道的一件事:从(包括)数字 11 开始,传入数据中的所有内容都可以是可选的(但如果在可选数字之后有有效的东西,那么这个数字必然是“-”)。另外,如果你有“-”直到最后,引用可以被截断。例如 :

A2--L3500XM-----可以是A2--L3500XM

D4--L4652Z-4----可以是D4--L4652Z-4

A3--L5020MW4---AA3--L5020MW4---A

这些都是有效的参考。这是我迄今为止一直在做的事情:

[DA][1-5][V\-][AN\-][LMFGHN][\d]{4}[H-RT-ZBD]([H-RT-ZBD\-]?)(?(-1)[1-4\-]?)

我到了第 11 位,但这就是它变得棘手的地方。这是我的示例参考:

D1--L6000T-4       VALID
D1--L6000T         VALID
D1--L6000T4-----   INVALID
D1--L6000T-----    VALID

我已经用 VALID 注释了应该匹配的那些。我的问题是我的正则表达式全部采用(按预期截断“-”),但第三个应该匹配,因为 4 对第 11 位无效。我认为它采用 4 的原因是因为第 10 位是可选的,这意味着如果它不存在,我猜 -1 可能是指[H-RT-ZBD],其中包含几乎相同的字符?所以我在这里有点迷茫,我的问题是:我怎样才能做到这一点?有没有更简单的方法 ?甚至可以做到吗?

模式规则:

digit number : pattern
1 :   [DA]
2 :   [1-5]
3 :   [V-]
4 :   [AN-]
5 :   [LMFGHN]
6-9 : \d{4}
10 :  [H-RT-ZBD]
11 :  [H-RT-ZBD-]?
12 :  [1-4-]?
13 :  [KRX-]?
14 :  [CPW-]?
15 :  [MT-]?
16 :  [A-C-]?

通常,要匹配的输入字符串是一个包含大量空格和可能任何字符的普通表。

4

3 回答 3

2

以下正则表达式为您提供的所有规则和输入评估 true。您可以复制整个块并使用它,包括注释。

$string = "D1--L6000T4------ A3--L5020MW4---A D4--L4652Z-4---- D1--L6000T";

preg_match_all(
    '/(                  # Matching the start of the group
       [DA]              # Digit 1
       [1-5]             # Digit 2
       [V-]              # Digit 3
       [AN-]             # Digit 4
       [LMFGHN]          # Digit 5
       \d{4}             # Digit 6-9
       [H-RT-ZBD]        # Digit 10
    (?:[H-RT-ZBD-]       # Digit 11
    (?:[1-4-]            # Digit 12
    (?:[KRX-]            # Digit 13
    (?:[CPW-]            # Digit 14
    (?:[MT-]             # Digit 15
       [A-C-]            # Digit 16
    ?)?)?)?)?)?)         # Closing all the groups and alternations
    (?:(?=(\s|%20))|$    # Matching a space (even %20) or end of string. This is done to exclude partial matches.
    )/x', 
    $string, $matches
);

var_dump($matches);

关于我所做的更改的一些注释。

删除了字符组内破折号的转义。如果将它放在没有标记间隔的地方,它将像任何其他字符一样。

删除了[ ]周围的字符组\d

我没有选择第 11 位数字,而是写出该模式与 11 和 out 的不同之处。组的递归可选性确保如果第 12 个字符能够匹配,它需要第 11 个,依此类推。

终点边界

我已添加(?:(?=(\s|%20))|$到正则表达式的末尾,以确保该模式仅在没有以下字符时才匹配,否则这些字符将成为该模式的一部分。D1--L6000T这样做是减少部分匹配的唯一方法,例如D1--L6000T4------当您不能保证每个模式都将由空格分隔时。坦率地说,我觉得很奇怪。

检索匹配

构造正则表达式,以便完整模式可用作匹配部分,或匹配数组中的索引 0。

于 2013-05-28T08:52:15.237 回答
1

我猜 -1 可能指的是[H-RT-ZBD],其中包含几乎相同的字符?

不,它确实指的([H-RT-ZBD\-]?)是最后一个捕获组。然而,它总是匹配,因为该字母是可选的 - 然后捕获空字符串。你的条件是真的。

我想你应该让你的字符捕获组可选而不是捕获一个可选字符(有关差异,请参见http://www.regular-expressions.info/captureall.html)。所以试试

[DA][1-5][V-][AN-][LMFGHN]\d{4}[H-RT-ZBD]([H-RT-ZBD-])?(?(-1)([1-4-]))?(?(-1)([KRX-]))?(?(-1)([CPW-]))?(?(-1)([MT-]))?(?(-1)[A-C-])?

如果您不需要捕获组,您可以通过使用更自然的嵌套而不是条件来简化表达式:

[DA][1-5][V-][AN-][LMFGHN]\d{4}[H-RT-ZBD](?:[H-RT-ZBD-](?:[1-4-](?:[KRX-](?:[CPW-](?:[MT-][A-C-]?)?)?)?)?)?

当然,在任何情况下你都必须锚定你的表达式,这样它就不会匹配无效输入的有效子部分:-)你可以将它包装在^+ $(?<!\S)+(?!\S)(?<=\s|^)+(?=\s|$)中。

于 2013-05-28T08:51:53.937 回答
-1

您应该只使用正则表达式来选择可能的模式,以便从原始字符串中提取它们。

在您拥有选定的潜在模式数组后,使用IFSWITCH语句substr以及其他字符串比较函数来确定每个模式是否有效。

否则,您可能会编写过于复杂的正则表达式,这将花费太长时间来调试、太长时间来更新并且之后没人会想要使用您的代码。

于 2013-05-28T08:24:35.673 回答