Найти все индексы подстроки в строке
Я хочу быть в состоянии найти индекс всех вхождений подстроки в большую строку, используя Ruby. Например: все "в" в "Эйнштейне"
str = "Einstein"
str.index("in") #returns only 1
str.scan("in") #returns ["in","in"]
#desired output would be [1, 6]
4 ответа
Стандартный хак это:
"Einstein".enum_for(:scan, /(?=in)/).map { Regexp.last_match.offset(0).first }
#=> [1, 6]
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]
является желаемым возвращаемым значением во втором примере), этот ответ явно неуместен.
Это более подробное решение, которое дает преимущество не полагаться на глобальную ценность:
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
Первые показатели.
Кроме того, если вам может потребоваться больше информации, чем просто индексы, вы можете вернуть Enumerator
из MatchData
и извлечь индексы:
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
,
#Recursive Function
def indexes string, sub_string, start=0
index = string[start..-1].index(sub_string)
return [] unless index
[index+start] + indexes(string,sub_string,index+start+1)
end
# Для лучшего использования я бы открыл
String
класс
class String
def indexes sub_string,start=0
index = self[start..-1].index(sub_string)
return [] unless index
[index+start] + indexes(sub_string,index+start+1)
end
end
Таким образом мы можем вызвать таким образом:
"Einstein".indexes("in") #=> [1, 6]