PCRE php 正则表达式以正确匹配组
PCRE php regex to match groups correctly
我有以下示例文本:
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
但描述、值 1 和值 2 的长度各不相同,虽然我有值 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 url 来展示我尝试过的东西
https://regex101.com/r/Smh2KA/3
提前致谢。
这个应该有效:
(?<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,})
这是一个Demo based on your initial text and here一个更简单的
它 returns 35 个符合预期,包括这个有点棘手,因为 value1 和 value2 没有被简单的 space 分隔:
31069 UDP GAMER - IGW10(I7 3.4,8,1,DVD,PV)4 499.0 04 999.90
我建议像这样的正则表达式
\b(?<code>\d{5})\s+(?<description>.*?)\s+(?<value1>\d[,\d\s]*\.\d{2})\s*(?<value2>\d[,\d\s]*\.\d{2})
有评论的版本:
\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
注意:如果您的浮点值(value1 和 value2)包含 ,
作为千位分隔符和 .
作为小数分隔符,请将它们的模式调整为\d[,\d]*\.\d+
。如果千位分隔符是 space,请使用 \d[\d\s]*\.\d+
。如果千位分隔符是 space 而小数分隔符是逗号,则使用 \d[\d\s]*,\d+
。等等等等。
您可以使用这种模式:
$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';
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 和前一个模式搜索每个部分(你可以用 ^
锚点代替\b
):
$parts = preg_split('~\b(?=\d{5}\b)~', $str);
$result = [];
foreach ($parts as $part) {
preg_match($pattern, $part, $m);
$result[] = [$m['id'], $m['desc'], $m['val1'], $m['val2']];
}
我有以下示例文本:
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
但描述、值 1 和值 2 的长度各不相同,虽然我有值 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 url 来展示我尝试过的东西 https://regex101.com/r/Smh2KA/3
提前致谢。
这个应该有效:
(?<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,})
这是一个Demo based on your initial text and here一个更简单的
它 returns 35 个符合预期,包括这个有点棘手,因为 value1 和 value2 没有被简单的 space 分隔:
31069 UDP GAMER - IGW10(I7 3.4,8,1,DVD,PV)4 499.0 04 999.90
我建议像这样的正则表达式
\b(?<code>\d{5})\s+(?<description>.*?)\s+(?<value1>\d[,\d\s]*\.\d{2})\s*(?<value2>\d[,\d\s]*\.\d{2})
有评论的版本:
\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
注意:如果您的浮点值(value1 和 value2)包含 ,
作为千位分隔符和 .
作为小数分隔符,请将它们的模式调整为\d[,\d]*\.\d+
。如果千位分隔符是 space,请使用 \d[\d\s]*\.\d+
。如果千位分隔符是 space 而小数分隔符是逗号,则使用 \d[\d\s]*,\d+
。等等等等。
您可以使用这种模式:
$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';
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 和前一个模式搜索每个部分(你可以用 ^
锚点代替\b
):
$parts = preg_split('~\b(?=\d{5}\b)~', $str);
$result = [];
foreach ($parts as $part) {
preg_match($pattern, $part, $m);
$result[] = [$m['id'], $m['desc'], $m['val1'], $m['val2']];
}