PCRE php正则表达式正确匹配组

时间:2017-04-11 16:40:17

标签: php regex pcre

我有以下示例文本:

tabela de Preço 18654 TONER XEROX 106R01632 MA(6000/6010 117.90 129.90 18656 TONER XEROX 106R01634 PR 6000/6010 179.00 199.00 UDP COMPUT ADORES IBYTE 32607 UDP A - GCL(CDCP 2.41,2,500) 747.00 829.90 32148 UDP A - GCL(CDCP 2.41,2,500) 747.00 829.90 32149 UDP A - GCL(CDCP 2.41,4,500,DVD) 769.90 879.00 32555 UDP A - GCL(CDCP 2.41,4,500,DVD) 769.90 879.00 32490 UDP A - ICL(CDCP 2.41,2,500) 747.00 829.90 32150 UDP A - ICL(CDCP 2.41,2,500) 747.00 829.90 32024 UDP A - ICW10(CDC 2.8,4,500,DVD) 1 260.001 399.90 32445 UDP A - ICW10(CDC 2.8,4,500,DVD) 1 260.001 399.90 31060 UDP A - ISW10PRO(CDCP 2.41,4,500)SLI1 349.901 549.90 32356 UDP F - GCL(I3 6G 3.7,4,500,DVD,LT) 1 699.001 929.90 

我必须在以下小组中进行匹配:

code, description,value1,value2

使用该摘录作为来源:

"18654 TONER XEROX 106R01632 MA(6000/6010 117.90 129.90"

它是一个产品,我需要按如下方式解析它:

"18654" is the code
"TONER XEROX 106R01632 MA(6000/6010" is the description
"117.90" is the value1
"129.90" is the value2

但描述,value1和value2的长度各不相同,而我的产品的值为1,如“117.90”,我也有“1 699.00”和“90.00”。

我尝试使用以下正则表达式捕获组,但它正确匹配某些不是整个源字符串:

(?<code>\d{5})\s{1}(?<description>.{20,35})\s{1}(?<value1>\d{2,3}\.\d{2})\s{1}(?<value2>\d{2,3}\.\d{2})

如何使用pcre(php)?

为此示例源字符串中的每个产品正确捕获组

我有以下regex101.com网址来显示我尝试过的内容 https://regex101.com/r/Smh2KA/3

提前致谢。

3 个答案:

答案 0 :(得分:1)

我建议使用像

这样的正则表达式
\b(?<code>\d{5})\s+(?<description>.*?)\s+(?<value1>\d[,\d\s]*\.\d{2})\s*(?<value2>\d[,\d\s]*\.\d{2})

请参阅regex demo

带有评论的版本:

\b                           # leading word boundary
(?<code>\d{5})               # 5 digits
\s+                          # 1+ whitespaces
(?<description>.*?)          # any 0+ non-line break chars
\s+                          # 1+ whitespaces
(?<value1>\d[,\d\s]*\.\d{2}) # a float number with 2-digit fractional part
\s*                          # 0+ whitespaces
(?<value2>\d[,\d\s]*\.\d{2}) # a float number

注意:如果您的浮点值(值1和值2)包含,作为千位分隔符而.作为小数点分隔符,则将其模式设为\d[,\d]*\.\d+ 。如果千位分隔符是空格,请使用\d[\d\s]*\.\d+。如果千位分隔符是空格而小数分隔符是逗号,请使用\d[\d\s]*,\d+。依此类推。

答案 1 :(得分:1)

您可以使用此模式:

$pattern = '~\b (?<id>\d{5}) \s
           (?<desc>.*?) \s*+
           (?<val1>
               (?: \d \s*(?=[\d\s]*\.\d\s?\d\s*(?<c>(?(c)\g{c})\s*\d)) )+
               \.\d\s?\d
           ) \s*
           (?<val2>\g{c}\d?\.\d{2})~x';

demo

val1中的子模式检查val1的整数部分中的每个数字是否有val2中整数部分的数字。这就是为什么这部分有点复杂。但优点是描述部分和第一个值之间不再存在混淆。

val1子模式详细信息:

(?:
    \d \s* # 1 digit in val1 (and an eventual space)
    (?= # lookahead that checks if for this digit there's also
        # a digit in val2
        [\d\s]*\.\d\s?\d\s* # reach val2
        (?<c> # open a capture group c
             (?(c)\g{c}) # conditional: if the capture group c has already captured
                         # something then start the group with the backreference \g{c}
                         # (this means that the non-captured group has been repeated
                         # at least once)
             \s*\d       # add the next digit to c
        )
    )
)+ # repeat the non-capturing group
\.\d\s?\d

请注意,此模式需要很多步骤才能成功。如果你需要在一个大输入上使用它,我建议在每个代码之前拆分字符串,然后用preg_match和前一个模式搜索每个部分(你可以用^锚而不是{{1 }}):

\b

答案 2 :(得分:0)

这个应该有效:

(?<code>\d{5})\s+(?<description>((?!\d{2,}\.\d{1,}).)*)\s+(?<value1>\d{2,3}\.\d{1,})((?!\d{2,}\.\d{1,}).)*(?<value2>\d{2,}\.\d{1,})

以下是基于您的初始文字的Demohere更简单的文字

它按预期返回35个匹配,包括这个有点棘手,因为value1和value2没有用简单空格分隔:

31069 UDP GAMER - IGW10(I7 3.4,8,1,DVD,PV)4 499.0 04 999.90