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.


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/)

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.


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"]

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 }

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.