Cómo hacer coincidir todas las ocurrencias de una expresión regular

586

¿Hay alguna forma rápida de encontrar todas las coincidencias de una expresión regular en Ruby? Miré a través del objeto Regex en Ruby STL y busqué en Google en vano.

Chris Bunch
fuente
3
Leí que así es como puedo buscar en una cadena todos los patrones de
expresiones

Respuestas:

821

Usar scandebería hacer el truco:

string.scan(/regex/)
Vaquero
fuente
99
¿Pero qué hay de este caso? "match me!". scan (/.../) = ["mat", "ch" "me!" ], pero todas las apariciones de /.../ serían ["mat", "atc", "tch", "ch", ...]
Michael Dickens
13
No, no lo sería. /.../ es una expresión regular codiciosa normal. No retrocederá en el contenido coincidente. podrías intentar usar una expresión regular perezosa, pero incluso eso probablemente no sea suficiente. eche un vistazo a regexp doc ruby-doc.org/core-1.9.3/Regexp.html para expresar correctamente su regexp :)
Jean
49
esto parece un Ruby WTF ... ¿por qué está esto en String en lugar de Regexp con las otras cosas de regexp? Ni siquiera se menciona en los documentos de Regexp
Anentropic el
99
Supongo que es porque está definido y llamado en String, no en Regex ... Pero en realidad tiene sentido. Puede escribir una expresión regular para capturar todas las coincidencias utilizando Regex # match e iterar sobre los grupos capturados. Aquí escribe una función de coincidencia parcial y desea que se aplique varias veces en una cadena determinada, esto no es responsabilidad de Regexp. Le sugiero que verifique la implementación del escaneo para una mejor comprensión: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Jean
99
@MichaelDickens: en este caso, puede usar /(?=(...))/.
Konrad Borowski
67

Para encontrar todas las cadenas coincidentes, use el scanmétodo de String .

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

Si lo desea, MatchDataque es el tipo de objeto devuelto por el matchmétodo Regexp , use:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

El beneficio de usar MatchDataes que puedes usar métodos como offset:

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

Vea estas preguntas si desea saber más:

La lectura sobre variables especiales $&, $', $1, $2en Ruby será útil también.

sudo bangbang
fuente
12

si tienes una expresión regular con grupos:

str="A 54mpl3 string w1th 7 numbers scatter3r ar0und"
re=/(\d+)[m-t]/

puedes usar el scanmétodo de String para encontrar grupos coincidentes:

str.scan re
#> [["54"], ["1"], ["3"]]

Para encontrar el patrón correspondiente:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]
MVP
fuente
str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]es más idiomático questr.to_enum(:scan,re).map {$&}
el Hombre de hojalata
Tal vez lo malinterpretaste. La expresión regular del ejemplo de un usuario que respondí fue: /(\d+)[m-t]/no /\d+[m-t]/Escribir: re = /(\d+)[m-t]/; str.scan(re)es lo mismo str.scan(/(\d+)[mt]/)pero obtengo #> [["" 54 "], [" 1 "], [" 3 "]]y no "54m", "1t", "3r"]La pregunta fue: si tengo una expresión regular con un grupo y quiero capturar todos los patrones sin cambiar el patrón regular expresión (dejando el grupo), ¿cómo puedo hacerlo? En este sentido, una posible solución, aunque un poco críptica y difícil de leer, fue:str.to_enum(:scan,re).map {$&}
MVP
-1

Puedes usar string.scan(your_regex).flatten. Si su expresión regular contiene grupos, regresará en una única matriz simple.

string = "A 54mpl3 string w1th 7 numbers scatter3r ar0und"
your_regex = /(\d+)[m-t]/
string.scan(your_regex).flatten
=> ["54", "1", "3"]

Regex también puede ser un grupo con nombre.

string = 'group_photo.jpg'
regex = /\A(?<name>.*)\.(?<ext>.*)\z/
string.scan(regex).flatten

También puede usar gsub, es solo una forma más si desea MatchData.

str.gsub(/\d/).map{ Regexp.last_match }
Datt
fuente
Elimine la agrupación de your_regex = /(\d+)[m-t]/y no necesitará usarla flatten. Su ejemplo final utiliza lo last_matchque en este caso es probablemente seguro, pero es global y posiblemente podría sobrescribirse si se combinara alguna expresión regular antes de llamar last_match. En cambio, probablemente sea más seguro usarlo string.match(regex).captures # => ["group_photo", "jpg"]o string.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]como se muestra en otras respuestas, dependiendo del patrón y las necesidades.
El hombre de hojalata