2017-04-10 149 views
2

我希望能够使用Ruby在更大的字符串中找到所有出现的子字符串的索引。例如为:所有“在”中的“爱因斯坦”查找字符串中子字符串的所有索引

str = "Einstein" 
str.index("in") #returns only 1 
str.scan("in") #returns ["in","in"] 
#desired output would be [1, 6] 

回答

3

标准的手段是:

"Einstein".enum_for(:scan, /(?=in)/).map { Regexp.last_match.offset(0).first } 
#=> [1, 6] 
+0

不错,一个。注意''nnnn“.enum_for(:scan,/nn/).map {Regexp.last_match.offset(0).first}#=> [0,2]'。如果'[0,1,2]'是所需的返回值,则将正则表达式('/ nn /')更改为'/(?= nn)/'。 –

+0

好点,@Cary。我想在大多数情况下,我们希望第二个更新。 – tokland

2
def indices_of_matches(str, target) 
    sz = target.size 
    (0..str.size-sz).select { |i| str[i,sz] == target } 
end 

indices_of_matches('Einstein', 'in') 
    #=> [1, 6] 
indices_of_matches('nnnn', 'nn') 
    #=> [0, 1, 2] 

第二个例子反映了我关于重叠字符串的处理作出一个假设。如果不考虑重叠字符串(即,第二个示例中[0, 2]是期望的返回值),则此答案显然不合适。

+0

简单而干净,可能我会用这个。 – tokland

2

这是一个更详细的解决方案,它带来了不依赖于全球价值的优势:

def indices(string, regex) 
    position = 0 
    Enumerator.new do |yielder| 
    while match = regex.match(string, position) 
     yielder << match.begin(0) 
     position = match.end(0) 
    end 
    end 
end 

p indices("Einstein", /in/).to_a 
# [1, 6] 

它输出Enumerator,所以你也可以懒洋洋地使用它,或者只取n第一指标。

另外,如果你可能需要的不仅仅是指数的更多信息,你可以返回的MatchDataEnumerator并提取指数:

def matches(string, regex) 
    position = 0 
    Enumerator.new do |yielder| 
    while match = regex.match(string, position) 
     yielder << match 
     position = match.end(0) 
    end 
    end 
end 

p matches("Einstein", /in/).map{ |match| match.begin(0) } 
# [1, 6] 

要获取@Cary描述的行为,你可以替换最后在position = match.begin(0) + 1的区块内。

相关问题