2012-02-16 11 views
85

Actualmente estoy tratando de volver a convertir una cadena en varias variables. Ejemplo cadena:
Ruby Regexp group matching, asigne variables en 1 línea

ryan_string = "RyanOnRails: This is a test" 

he emparejado con esta expresión regular, con 3 grupos:

ryan_group = ryan_string.scan(/(^.*)(:)(.*)/i) 

ahora para acceder a cada grupo que tengo que hacer algo como esto:

ryan_group[0][0] (first group) RyanOnRails 
ryan_group[0][1] (second group) : 
ryan_group[0][2] (third group) This is a test 

Esto parece bastante ridículo y parece que estoy haciendo algo mal. Sería capaz de hacer algo como esto:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i) 

¿Esto es posible? ¿O hay una forma mejor que cómo lo estoy haciendo?

Respuesta

146

Usted no quiere scan para esto, ya que tiene poco sentido. Puede usar String#match que devolverá un objeto MatchData, luego puede llamar al #captures para devolver una Matriz de capturas. Algo como esto:

#!/usr/bin/env ruby 

string = "RyanOnRails: This is a test" 
one, two, three = string.match(/(^.*)(:)(.*)/i).captures 

p one #=> "RyanOnRails" 
p two #=> ":" 
p three #=> " This is a test" 

en cuenta que si no se encuentra ninguna coincidencia, String#match volverá nula, por lo que algo como esto podría funcionar mejor:

if match = string.match(/(^.*)(:)(.*)/i) 
    one, two, three = match.captures 
end 

Aunque scan no tienen mucho sentido para esto. Todavía hace el trabajo, solo necesita aplanar la matriz devuelta primero. one, two, three = string.scan(/(^.*)(:)(.*)/i).flatten

+3

Ten en cuenta que si no se encuentran coincidencias, los rendimientos de los partidos nil y se obtiene una NilError. Si se encuentra en los carriles, sugiero que cambie: 'uno, dos, tres = String.match (/(^.*)(:)(.*)/ i) .captures' en: 'uno, dos, tres = String.match (/(^.*)(:)(.*)/ i) .try (: capturas)' –

+4

@AndreaSalicetti que he editado mi post, no estoy añadiendo código específico de Rails, así que lo he alterado con una versión para manejar el objeto nil devuelto –

+0

Limpio y ordenado, ¡está bien! –

28

Usted podría utilizar Match o = ~ lugar que le daría un solo partido y se podía o bien acceder a los datos de los partidos de la misma manera o simplemente utilizar las variables especiales de concordancia $ 1, $ 2, $ 3

Algo así como:

if ryan_string =~ /(^.*)(:)(.*)/i 
    first = $1 
    third = $3 
end 
+0

wow. rubí. Yo no puedo dejar de sorprenderme. – Gaston

+1

@Gaston que en realidad es la sintaxis de expresiones regulares original, procedente de Perl :) – ohaleck

2

scan() encontrará todas las coincidencias no superpuestas de la expresión regular en su cadena, por lo que en lugar de devolver una matriz de sus grupos como parece estar esperando, devuelve una matriz de matrices.

usted es probablemente mejor usar match(), y luego conseguir el conjunto de capturas utilizando MatchData#captures:

g1, g2, g3 = ryan_string.match(/(^.*)(:)(.*)/i).captures 

Sin embargo también se puede hacer esto con scan() si quería:

g1, g2, g3 = ryan_string.scan(/(^.*)(:)(.*)/i)[0] 
23

Usted puede nombrar sus coincidencias capturadas

string = "RyanOnRails: This is a test" 
/(?<one>^.*)(?<two>:)(?<three>.*)/i =~ string 
puts one, two, three 

No funciona si invierte el orden de la cadena y la expresión regular.

2

Tienes que decidir si es una buena idea, pero ruby ​​regexp puede (automágicamente) definir variables locales para tí!

todavía no estoy seguro de si esta característica es impresionante o simplemente totalmente loco, pero su expresión regular puede definir variables locales.

ryan_string = "RyanOnRails: This is a test" 
/^(?<webframework>.*)(?<colon>:)(?<rest>)/ =~ ryan_string^ 
# This defined three variables for you. Crazy, but true. 
webframework # => "RyanOnRails" 
puts "W: #{webframework} , C: #{colon}, R: #{rest}" 

(Tome un vistazo a http://ruby-doc.org/core-2.1.1/Regexp.html, busque "variable local").

Nota: Como se señaló en un comentario, veo que hay una respuesta similar y anterior a esta pregunta por @toonsend (https://stackoverflow.com/a/21412455). No creo que estuviera "robando", pero si quiere ser justo con los elogios y honrar la primera respuesta, siéntase libre :) Espero que ningún animal haya sido dañado.

+0

Esta respuesta se asemeja mucho a http://stackoverflow.com/a/21412455/525478, que es más de un año mayor ... –

+0

@BradWerth Supongo que simplemente no lo vi. Pero actualicé mi respuesta para incluir sus inquietudes. – Felix