7

我需要一个正则表达式,它只选择那些不以特定扩展名(如 .png 或 .css)结尾的 URL 字符串。

我测试了以下内容:

1)这个使用负面的lookbehind:

(?<!\.png|\.css)$

https://regex101.com/r/tW4fO5/1

2)另一个使用负前瞻:

^(?!.*[.]png|.*[.]css$).*$

https://regex101.com/r/qZ7vA4/1

两者似乎都可以正常工作,但据说 #1(负后瞻)需要 436 步(见链接),而 #2(负前瞻)据说需要 173 步。

所以我的问题是:这是什么意思?会不会影响表演?

最后,这两个正则表达式在功能上真的等效吗?

编辑:解决方案摘要

总结一下,考虑到要通过正则表达式排除的字符串结尾的完整列表(典型的场景是 Web 服务器设置,其中静态资源由 apache 提供,而动态资源由不同的引擎提供 - 在我的情况下:php-fpm)。

PCRE 正则表达式有两个选项:

1)消极回顾

$(?<!\.(?:ico|gif|jpg|png|css|rss|xml|htm|pdf|zip|txt|ttf)$|(?:js|gz)$|(?:html|woff)$)

https://regex101.com/r/eU9fI6/1

请注意,我使用了几个 OR-ed 后视,因为负后视需要固定宽度的模式(即:您不能混合不同长度的模式)。这使得这个选项的编写更加复杂。此外,我认为这会降低其性能。

2)负前瞻

^(?!.*[.](?:js|ico|gif|jpg|png|css|rss|xml|htm|html|pdf|zip|gz|txt|ttf|woff)$).*$

https://regex101.com/r/dP7uD9/1

前瞻略快于后瞻。这是进行 100 万次迭代的测试结果:

后视时间 = 18.469825983047 秒
前瞻时间 = 14.316685199738 秒

如果我没有可变长度模式的问题,我会选择后视,因为它看起来更紧凑。无论如何,任何一个都很好。最后,我进行了前瞻:

<LocationMatch "^(?!.*[.](?:js|ico|gif|jpg|png|css|rss|xml|htm|html|pdf|zip|gz|txt|ttf|woff)$).*$">
    SetHandler "proxy:unix:/var/run/php5-fpm.sock|fcgi://www/srv/www/gioplet/web/public/index.php"
</LocationMatch>
4

2 回答 2

4

会不会影响表演?

在大多数情况下,正则表达式查找匹配项所需的步骤越多,性能就越慢。尽管它还取决于您稍后将在哪个平台上使用正则表达式(例如,如果您使用 regex101.com 测试用于在 .NET 中使用的正则表达式,这并不意味着它会导致灾难性回溯,延迟点匹配正则表达式失败并出现长文本)。

这两个正则表达式在功能上真的等效吗?

不,他们不是。(?<!\.png|\.css)$查找前面没有.pngor的行尾.css^(?!.*[.]png|.*[.]css$).*$查找不包含 .png的行或不以 .结尾 .css的行。要使它们“等效”(即,如果要确保以.pngor结尾的行.css不匹配),请使用

^(?!.*[.](?:png|css)$).*$
         ^^^^^^^^^^^^

确保在两者之后负前瞻中$都选中了。png css

正则表达式之间仍然存在差异:第一个将匹配行尾,第二个将匹配整行。

有没有办法加快后视解决方案?

请注意,模式 1 中的后向检查在字符串内的每个位置进行检查。模式 2 中的前瞻仅在字符串的开头检查一次。这就是为什么锚定前瞻解决方案在一种情况下会更快的原因 - 如果您不能使用仅在少数正则表达式(例如 .NET)中可用的 RightToLeft 修饰符。

$(?<!\.(?:png|css)$)后视解决方案比模式 1更快,因为后视模式在到达字符串/行的末尾后只检查一次。尽管如此,这需要更多的步骤,因为执行后向比前向更昂贵。

要真正找出最快的解决方案,您需要在您的环境中设置性能测试。

于 2016-02-18T08:49:52.407 回答
4

第二个或第lookahead一个更快。记住步骤数不是正确的方法。请参阅Stackoverflow 问题: atomic-groups-clarity

我已经在 python 上使用timeit. 脚本是

import timeit
s1="""
import re
re.findall(r"^.*(?<!\.png|\.css)$",x,re.M)"""

s2="""
import re
re.findall(r"^(?!.*[.]png$|.*[.]css$).*$",x,re.M)"""

print timeit.timeit(s1,number=1000000,setup='x="""http://gioplet/articles\nhttp://gioplet/img/logo.png\nhttp://gioplet/index.php\nhttp://gioplet/css/main.css"""')

print timeit.timeit(s2,number=1000000,setup='x="""http://gioplet/articles\nhttp://gioplet/img/logo.png\nhttp://gioplet/index.php\nhttp://gioplet/css/main.css"""')

输出:

8.72536265265
7.09159428305
于 2016-02-18T09:48:36.217 回答