¿Una forma más rubí de extraer correos electrónicos de una cadena?

Tengo la siguiente expresión regular y función para extraer correos electrónicos a una matriz y, mientras funciona, me parece menos que óptimo. ¿Alguna sugerencia sobre cómo podría aprobar esto?

@emails = []
matches = @text_document.scan(/\+'(\S+@\S+|\{(?:\w+, *)+\w+\}@[\w.-]+)'/i)
matches.each {|m| m[0].split(',').each {|email| @emails << email  }  }

Específicamente, estoy buscando algo mejor que cada uno anidado.

¡Salud

EDITAR Para ser completamente justo, ya que me gustaron ambas respuestas, les di a ambas una ejecución justa, pero dado que concat es un poco más rápido y más corto, lo marcaré como la respuesta.

require 'benchmark'

CONSTANT = 1
BenchTimes = 1_000_000
EMAILS = "+'one.emaili@domain.com,another.email@domain.se'"

def email
end

def bm_concat
  emails = []
  EMAILS.scan(/\+'(\S+@\S+|\{(?:\w+, *)+\w+\}@[\w.-]+)'/i) do |matches|
    matches.each {|m| emails.concat(m.split(','))}
  end

end

def bm_inject
  emails = []
  EMAILS.scan(/\+'(\S+@\S+|\{(?:\w+, *)+\w+\}@[\w.-]+)'/i) do |matches|
    matches.inject([]) {|arr, mails| emails.concat(mails.split(',')) }
  end

end

Benchmark.bmbm do |bm|
  bm.report("inject:") { BenchTimes.times { bm_inject } }
  bm.report("concat:") { BenchTimes.times { bm_concat } }
end

Produce el siguiente resultado:

Rehearsal -------------------------------------------
inject:  11.030000   0.060000  11.090000 ( 11.145898)
concat:   9.660000   0.050000   9.710000 (  9.761068)
--------------------------------- total: 20.800000sec

              user     system      total        real
inject:  11.620000   0.060000  11.680000 ( 11.795601)
concat:  10.510000   0.050000  10.560000 ( 10.678999)

preguntado el 31 de julio de 12 a las 15:07

@Polynomial: Esa expresión regular de correo electrónico vinculada es de miedo. No quiero ser el mantenedor de esa biblioteca. -

Acordado. Encuentre una biblioteca, o simplemente espere que no obtenga direcciones como hGy∂@olé.museum. -

No estoy preocupado por la expresión regular en absoluto, es el matches.each(|m| m[0].split(',').each {} Quiero mejorar. No podría importarme menos si el correo electrónico es válido o no. -

@mhenrixon: cambié mi respuesta para responder a su pregunta refinada. -

2 Respuestas

Puedes refactorizar el matches.each a esto:

matches.each {|m| @emails.concat(m[0].split(','))}

Respondido 01 ago 12, 00:08

No estoy preocupado por la expresión regular en absoluto, son los partidos. cada (|m| m[0].split(',').each {} Quiero mejorar. No me importa si el correo electrónico es válido no. - mhenrixon

Esto implementa la especificación de correo electrónico RFC822/RFC2822. También marca muchos como "válidos" que son rechazados por algunos sistemas de correo porque están locos. Teóricamente puede tener espacios en su dirección de correo electrónico, pero nunca he visto que esto se use en la práctica ni una sola vez. - tadman

@mhenrixon: deshacerse de la validación y brindarle una solución. - Linuxios

¡JA! Ahora obtuve 2 excelentes respuestas y tengo problemas para decidir cuál me gusta más. Gracias por la respuesta refinada :) - mhenrixon

Genial, no solo corto, sino también con un buen desempeño. Cambié el escaneo para enviar las coincidencias en el bloque para deshacerme de la otra matriz. - mhenrixon

Usar inyectar - http://ruby-doc.org/core-1.9.3/Enumerable.html#method-i-inject

@emails = matches.inject([]) do |emails, input| 
  emails += input.first.split(',')
end

Para tu información, las variables pasadas al bloque, los correos electrónicos se refieren a la matriz vacía pasada y la entrada se refiere a cada elemento de las coincidencias a medida que lo iteras.

Editar (Cómo usar inyectar):

REGEX = /\+'(\S+@\S+|\{(?:\w+, *)+\w+\}@[\w.-]+)'/i
def bm_inject
  emails = EMAILS.scan(REGEX).inject([]) do |arr, mails| 
    arr.concat mails.first.split(',')
  end
end

Respondido 01 ago 12, 22:08

por supuesto, también puede hacerlo en una línea con llaves para el bloque: ajcodez

¡Gracias por el consejo! Le di una oportunidad, pero dado que la solución de @Linuxios es un poco más rápida, eso se marcará como respuesta. - mhenrixon

buena decisión en el punto de referencia. Le insto a que revise la documentación para inyectar y vea mi edición para que la entienda en el futuro, ¡porque la está usando mal! - ajcodez

Muchas gracias por eso. ¡Prometo aprender a inyectar correctamente! He tenido la intención de hacerlo durante mucho tiempo, pero esta es la primera vez que todo tiene sentido para mí, así que nuevamente gracias por el ejemplo editado. Veo dónde me equivoqué. - mhenrixon

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.