正则表达式多次匹配子字符串

8
我有一个应用程序,它会根据Perl正则表达式来确定是显示下拉菜单还是简单的输入字段。因此,我必须检查正则表达式模式是否具有"外部形式"和子字符串。为此,我想出了几种解决方案。
对于输入模式"^(100|500|1000)$",应该得到一个带有三个条目(100、500和1000)的下拉菜单。我需要一个正则表达式来解析整个模式,以确定其是否是有效列表,以及一个实际的子字符串匹配正则表达式,因为我不知道如何多次匹配一个子字符串。这是我的正则表达式模式:
^\^\((?:((?:[^\|]|\\\|)+)(?:\||(?:\)\$$)))+

稍微简化一下,因为这个正则表达式有些模糊:

^\^\((?:([\w\d]+)(?:\||(?:\)\$$)))+

这段代码能够工作,但是只会存储最后一个子字符串(在这个例子中是1000),并且舍弃其他的部分。使用PCRE或者在线正则表达式工具进行了测试。为了获取实际的子字符串,例如下拉菜单字段,我需要:

(?:\^\()?((?:[^\|]|\\|)+)(?:\||(?:\)\$$))

再次简化:

再次简化:

(?:\^\()?([\w\d]+)(?:\||(?:\)\$$))

这个正则表达式可以找到子字符串,但不能匹配其他正则表达式的下拉菜单模式语法(例如,这个正则表达式也会匹配以"^(100|"开头的子字符串,如"100")。 我的问题是:有没有办法将这些正则表达式组合成一个只匹配1)整个模式语法和2)实际子字符串的模式?
谢谢!
Jeremy
附:抱歉如果这很明显,但今天我对所有这些正则表达式都感到混乱。
样本数据:
输入正则表达式:^(100|500|1000)$ 语法正确! 匹配的子字符串:100、500、1000 =>显示下拉菜单
输入正则表达式:^[0-9a-fA-F]+$ 语法错误! =>显示普通输入字段
输入正则表达式:^(foo|bar)$ 语法正确! 匹配的子字符串:"foo"、"bar" =>显示下拉菜单
输入正则表达式:^(foo|bar)[0-9]+$ 语法错误! =>显示普通输入字段

我不确定你的问题。你能提供一下你所拥有的样本数据以及你需要的输出吗? - Federico Piazza
谢谢你的帮助!我添加了一些样本数据。 - Jeremy
我已经根据您提供的数据更新了我的答案。 - Federico Piazza
2个回答

7
你可以通过两个步骤实现你的需求。
你可以使用以下正则表达式来验证格式:
\^\(\w+(?:\|\w+)*\)\$

演示链接

图片描述

一旦您验证了正确的字符串,您可以使用以下函数:

$str = "^(100|500|1000|2000|3000)$";
$arr = preg_split ("/\W+/" , $str, -1, PREG_SPLIT_NO_EMPTY);
print_r($arr);

输出:

Array
(
    [0] => 100
    [1] => 500
    [2] => 1000
    [3] => 2000
    [4] => 3000
)

谢谢!这简化了我的语法匹配正则表达式,但没有捕获子字符串(在本例中为“100”、“500”、“1000”)。您也可以帮我解决这个问题吗? - Jeremy
我修改了你的正则表达式为"^((\w+)(?:|(\w+))*)$"。这个表达式捕获了两个组,都是"\w+",而最后一个组将被覆盖为超过2个项(这在第一个测试字符串中发生)。 - Jeremy
@Jeremy,除了Lucas的答案之外,你也可以使用我的正则表达式\^((\w+)(?:\|(\w+))*)\$。与此同时,我会为此做出努力。 - Federico Piazza
@Jeremy 我认为你应该采用两步骤。首先使用我的正则表达式来验证模式,一旦验证通过,你就可以使用 preg_split ("/\W+/", string $subject) - Federico Piazza
再次感谢您的帮助。由于我的应用程序是用C语言编写的,所以我无法应用您的代码片段。但我会简化我的正则表达式。正如Alan所说,PCRE不能进行中间捕获。 - Jeremy

1

看起来你正在使用PCRE。

你可以利用PCRE_DUPNAMES选项,或者在模式前面加上(?J)选项。

这个选项使得PCRE记住每个匹配的捕获组的值,而不是丢弃除最后一个之外的所有内容。 (这是错误的, 详见评论)

不幸的是,据我所知,它不受在线测试工具的支持。我不知道你使用的是哪种语言,但是它也需要一些支持才能让你使用此功能。

来自PCRE文档

如果您想获取给定名称的所有捕获子字符串的完整详细信息,则必须使用pcre_get_stringtable_entries()函数。


1
我的应用程序是使用C语言编写的,确实会使用pcre。谢谢你的提示,我会尝试一下。 - Jeremy
我承认在查看PCRE文档后有些困惑。我原以为它类似于.NET的多个捕获,但现在我不太确定了。请告诉我。 - Lucas Trzesniewski
2
PCRE_DUPNAMES 允许您为两个或多个组分配相同的名称,即使这些组具有不同的编号。当匹配完成时,与该名称关联的值仍将只有一个,与组编号相同。 与 .NET 不同,PCRE 仍然无法提供检索重复组的中间捕获的方法。 - Alan Moore
@Lucas:我尝试了你的提示,但它们没有起作用 - PCRE仍然不会返回所有匹配项,就像Alan所说的那样。因此,我将按照Fede的建议简化我的正则表达式。 - Jeremy
@AlanMoore 我显然误解了关于这个的文档。太糟糕了,感谢您的澄清。Jeremy:抱歉给出了误导性的答案。 - Lucas Trzesniewski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接