regulares - ¿Cómo obtengo los datos de coincidencia para todas las apariciones de una expresión regular de Ruby en una cadena?
ruby regex validator (5)
Necesito el MatchData
para cada aparición de una expresión regular en una cadena. Esto es diferente al método de escaneo sugerido en Match All Occurrences of a Regex , ya que eso solo me da una matriz de cadenas (necesito MatchData completo, para obtener información de inicio y final, etc.).
input = "abc12def34ghijklmno567pqrs"
numbers = //d+/
numbers.match input # #<MatchData "12"> (only the first match)
input.scan numbers # ["12", "34", "567"] (all matches, but only the strings)
Sospecho que hay algún método que he pasado por alto. Sugerencias?
Lo pondré aquí por si acaso para hacer que el código esté disponible a través de la búsqueda de google respectiva:
input = "abc12def34ghijklmno567pqrs"
numbers = //d+/
input.gsub(numbers) { |m| p $~ }
El resultado es el siguiente:
⇒ #<MatchData "12">
⇒ #<MatchData "34">
⇒ #<MatchData "567">
Explicación detallada
Me sorprende que nadie haya mencionado la increíble clase StringScanner incluida en la biblioteca estándar de Ruby:
require ''strscan''
s = StringScanner.new(''abc12def34ghijklmno567pqrs'')
while s.skip_until(//d+/)
num, offset = s.matched.to_i, [s.pos - s.matched_size, s.pos - 1]
# ..
end
No, no le proporciona los objetos MatchData, pero le proporciona una interfaz basada en índices en la cadena.
Mi solución actual es agregar un método each_match a Regexp:
class Regexp
def each_match(str)
start = 0
while matchdata = self.match(str, start)
yield matchdata
start = matchdata.end(0)
end
end
end
Ahora puedo hacer:
numbers.each_match input do |match|
puts "Found #{match[0]} at #{match.begin(0)} until #{match.end(0)}"
end
Dime que hay una mejor manera.
Usted quiere
"abc12def34ghijklmno567pqrs".to_enum(:scan, //d+/).map { Regexp.last_match }
que te da
[#<MatchData "12">, #<MatchData "34">, #<MatchData "567">]
El "truco" es, como ve, construir un enumerador para obtener cada last_match.
input = "abc12def34ghijklmno567pqrs"
n = Regexp.new("//d+")
[n.match(input)].tap { |a| a << n.match(input,a.last().end(0)+1) until a.last().nil? }[0..-2]
=> [#<MatchData "12">, #<MatchData "34">, #<MatchData "567">]