Postgres贪婪正则表达式表现为不情愿



我写了几乎整个问题,然后找到了答案,所以我将把它放在这里的Q&A样式,因为所描述的行为对我来说似乎很惊讶。

这个正则表达式可以正常工作并将字符串分成三部分-数字部分和字母部分包围:

select regexp_replace('abc12345def', '^(.*?)([0-9]+)(.*)$', '{first="1" second="2" third="3"}');
{first="abc" second="12345" third="def"}

然而,在移除^$锚点后,我得到

select regexp_replace('abc12345def', '(.*?)([0-9]+)(.*)', '{first="1" second="2" third="3"}');
{first="abc" second="1" third=""}2345def

因为组2和组3有贪婪量词,我期望它们分别匹配12345def,因此返回相同的字符串。等效Java代码的行为如下:

System.out.println("abc12345def".replaceFirst("(.*?)([0-9]+)(.*)", "{first='$1' second='$2' third='$3'}"));
System.out.println("abc12345def".replaceFirst("^(.*?)([0-9]+)(.*)$", "{first='$1' second='$2' third='$3'}"));
{first='abc' second='12345' third='def'}
{first='abc' second='12345' third='def'}

为什么不工作?

小提琴

Postgres正则表达式中的贪婪度是作为一个整体设置的。与所提供的基本相同的示例可以在文档中找到解释:

SELECT regexp_match('abc01234xyz', '(.*?)(d+)(.*)');

Result: {abc,0,""}

这也不起作用,因为现在正则作为一个整体是非贪婪的,所以它会尽快结束整个匹配。我们可以通过强迫正则作为一个整体变得贪婪来得到我们想要的:…

解决方案是强制整个正则表达式为贪婪:

select regexp_replace('abc12345def', '(?:(.*?)([0-9]+)(.*)){1,1}', '{first="1" second="2" third="3"}');
{first="abc" second="12345" third="def"}

对我来说有点违反直觉,但行得通。

最新更新