我正在尝试找到一个匹配带有三个或更多重复段的URL的正则表达式(可能包含任意数量的目录),例如:
s1 = 'http://www.foo.com/bar/bar/bar/'
s2 = 'http://www.foo.com/baz/biz/baz/biz/baz/biz/etc'
s3 = '/foo/bar/foo/bar/foo/bar/'
并且不匹配以下网址:
s4 = '/foo/bar/foo/bar/foo/barbaz'
首先我尝试了:
re1 = /((.+\/)+)\1\1/
有效:
re1 === s1 #=> true
re1 === s2 #=> true
但随着段数的增加,正则表达式匹配呈指数级增长:
require 'benchmark'
Benchmark.bm do |b|
(10..15).each do |num|
str = '/foo/bar' * num
puts str
b.report("#{num} repeats:") { /((.+\/)+)\1\1/ === str }
end
end
user system total real
/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar
10 repeats: 0.060000 0.000000 0.060000 ( 0.054839)
/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar
11 repeats: 0.210000 0.000000 0.210000 ( 0.213492)
/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar
12 repeats: 0.870000 0.000000 0.870000 ( 0.871879)
/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar
13 repeats: 3.370000 0.010000 3.380000 ( 3.399224)
/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar
14 repeats: 13.580000 0.110000 13.690000 ( 13.790675)
/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar/foo/bar
15 repeats: 54.090000 0.210000 54.300000 ( 54.562672)
然后,我尝试了类似于给定here的正则表达式:
re2 = /(\/.+)(?=.*\1)\1\1/
没有性能问题,并匹配我想要匹配的字符串:
re2 === s3 #=> true
但也匹配我不希望它匹配的字符串,例如:
re2 === s4 #=> true, but should be false
我接近第二个正则表达式。我错过了什么?
答案 0 :(得分:2)
将-builmdmode=shared
更改为libstd.so
。这应该会降低正则表达式的复杂性,因为它不会尝试匹配“任何”字符。
shared
答案 1 :(得分:0)
定义
假设:
str = 'http://www.example.com/dog/baz/biz/baz/biz/baz/biz/cat/'
我们可以将'/dog'
,'/baz'
,'/biz'
等定义为段。 group 由一个或多个连续的段组成,例如'/dog'
,'/baz'
,'/dog/baz'
,'/baz'
,'/baz/biz'
, 'biz/baz'
,'/baz/biz/baz'
等。
问题
据我了解,问题在于确定给定的字符串是否包含三个(或更多)连续且相等的组,后跟一个正斜杠。 s2
符合以下子字符串的要求:
'/baz/biz/baz/biz/baz/biz/'
算法
我不相信可以使用一个正则表达式来确定,但是我们可以编写一个正则表达式来确定是否存在至少三个(或任意数量)连续的,相等的组。 每组的细分数。假设这是通过名为contiguous_fixed_group_size?
的方法完成的,该方法如下:
contiguous_fixed_group_size?(str, segments_per_group, nbr_groups)
并返回true
或false
。为确保字符串具有至少3个连续的相等组(对于给定值segments_per_group
),我们用nbr_groups = 3
调用此方法。我认为最好暂时推迟此方法的构建;就目前而言,假设它对我们可用。
我采用的方法是用不同的segments_per_group
值调用此方法,并确定该方法是否对于其中至少一个返回true
。
主要方法
第一步是确定字符串中的段数(其中str
保存上面给出的字符串):
r = /(?<!\/)\/(?!\/)/
nbr_segments = str.scan(r).size - 1
#=> 8
我们可以通过以 free-spacing 模式编写正则表达式来记录该正则表达式:
r = /
(?<!\/) # match is not to be preceded by '/' (negative lookbehind)
\/ # march '/'
(?!\/) # match is not to be followed by '/' (negative lookahead)
/x
环顾四周阻止'//'
中的str
匹配。
我们现在问自己,必须考虑的segments_per_group
的最大值是多少。因为我们要求:
nbr_groups * segments_per_group <= nbr_segments
随之而来:
segments_per_group <= nbr_segments/nbr_groups
其中右侧使用整数算术。对于nbr_groups = 3
,我们获得:
segments_per_group <= 8/3 => 2
因此,我们可以确定str
是否包含(至少)nbr_groups
个连续的,相等的组,如下所示:
(1..nbr_segments/nbr_groups).any? do |segs_per_group|
contiguous_fixed_group_size?(str, segs_per_group, nbr_groups)
end
#=> true
我们可以将其包装在一个方法中:
def contiguous?(str, nbr_groups)
nbr_segments = str.scan(/(?<!\/)\/(?!\/)/).size - 1
(1..nbr_segments/nbr_groups).any? do |segs_per_grp|
contiguous_fixed_group_size?(str, segs_per_grp, nbr_groups)
end
end
构造方法contiguous_fixed_group_size?
此方法可以编写如下:
def contiguous_fixed_group_size?(str, segments_per_group, nbr_groups)
r = /((?:\/[^\/]+){#{segments_per_group}})\1{#{nbr_groups-1}}/
str.match?(r)
end
对于
str = s2
segments_per_group = 2
nbr_groups = 3
正则表达式为:
r #=> /((?:\/[^\/]+){2})\1{2}\//
此处以自由行模式
r = /
(?<!\/) # match is not to be preceded by a forward slash
# (negative lookbehind)
( # begin capture group 1
(?: # begin non-capture group
\/[^\/]+ # match '/' followed by 1+ char other than '/'
) # end non-capture group
{#{segments_per_group}} # execute non-capture group segments_per_group times
) # end capture group 1
\1{#{nbr_groups-1}} # execute contents of capture group 1
# nbr_groups-1 times
\/ # match '/'
/x # free-spacing regex definition mode
示例
str
如上所述。
contiguous?(str, 3) #=> true
contiguous?(str, 2) #=> true
contiguous?(str, 1) #=> true
contiguous?(str, 4) #=> false
str = 'http://www.example.com/dog/baz/biz/baz/bix/baz/biz/cat/'
contiguous?(str, 3) #=> false
contiguous?(str, 2) #=> false
contiguous?(str, 1) #=> true