2011-09-23 7 views
7

Esta es una pregunta de dos partes. Dado que tiene una serie de cadenas que se pueden dividir en un carácter (por ejemplo, direcciones de correo electrónico en '@' o nombres de archivo en '.') ¿Cuál es la forma más efectiva de encontrar los caracteres antes del carácter dividido?Búsqueda de cadenas de rubíes: ¿cuál es más rápida división o expresiones regulares?

my_string.split(char)[0] 

o

my_string[/regex/] 

La segunda parte de la pregunta es ¿cuál es ¿cómo se escribe una expresión regular para conseguir todo antes de la primera instancia de un personaje. La expresión regular a continuación encuentra ciertos caracteres antes de un '.' (porque '.' no está en el patrón) pero esa era mi manera estrafalaria de llegar a una solución.

my_string[/[A-Za-z0-9\_-]+/] 

gracias!

+5

Dudo que haya una diferencia notable al tratar con las direcciones de correo electrónico (a menos que esté procesando millones por segundo ...). ¿Pero por qué no te mides a ti mismo y lo averiguas? –

Respuesta

12

La manera más fácil de responder a la primera parte es, como siempre, compararla con sus datos reales. Por ejemplo:

require 'benchmark' 
Benchmark.bm do |x| 
    x.report { 50000.times { a = '[email protected]'.split('@')[0] } } 
    x.report { 50000.times { a = '[email protected]'[/[^@]+/] } } 
end 

dice (en mi configuración):

 user  system  total  real 
    0.130000 0.010000 0.140000 ( 0.130946) 
    0.090000 0.000000 0.090000 ( 0.096260) 

Así que la solución de expresiones regulares se ve un poco más rápido, pero la diferencia es apenas perceptible incluso con 50 000 iteraciones. OTOH, la solución de expresiones regulares dice exactamente lo que quieres decir ("dame todo antes del primer @"), mientras que la solución split obtiene el resultado deseado de una manera un poco indirecta.

El enfoque split es probablemente más lento porque tiene que escanear toda la cuerda para dividirla en pedazos, luego construir una matriz de las piezas y finalmente extraer el primer elemento de la matriz y tirar el resto; No sé si la máquina virtual es lo suficientemente inteligente como para reconocer que no es necesario construir la matriz, así que eso es solo un poco de trabajo de adivinanza.

En lo que se refiere a su segunda pregunta, di lo que se refiere a:

my_string[/[^.]+/] 

Si quieres que todo antes del primer período y luego decir "todo hasta que un período" en lugar de "el primer trozo que se hace de estos personajes (que por casualidad no contienen un punto) ".

+0

Gracias por eso, no sabía que Ruby tenía una herramienta de referencia incorporada. En mi propia prueba descubrí que la expresión regular también es más rápida, siempre y cuando la primera subcadena tenga menos de 50 caracteres, después de esa división es más rápida. Por supuesto, como se mencionó, con pequeños sets apenas notamos la diferencia. – kreek

+1

@Kreek: Es por eso que pasas el tiempo SO, para aprender cosas nuevas :) Creo que es mejor comenzar expresando tus intenciones en el código lo más claramente posible y luego preocuparte por el rendimiento si realmente hay un problema. –

+0

@muistooshort no hay ningún efecto de caché o pagefaults? – Benjamin

4

partition será más rápido que split ya que no continuará comprobando después de la primera coincidencia.

Un slice regular con index será más rápido que una expresión regular slice.

La división de expresiones regulares también se ralentiza considerablemente a medida que la parte de la cadena antes de la coincidencia aumenta. Se vuelve más lento que la división original después de ~ 10 caracteres y luego se vuelve mucho peor a partir de ahí. Si tienes una Regexp sin una coincidencia + o *, creo que es un poco mejor.

require 'benchmark' 
n=1000000 

def bench n,email 
    printf "\n%s %s times\n", email, n 
    Benchmark.bm do |x| 
     x.report('split ') do n.times{ email.split('@')[0] } end 
     x.report('partition') do n.times{ email.partition('@').first } end 
     x.report('slice reg') do n.times{ email[/[^@]+/] } end 
     x.report('slice ind') do n.times{ email[0,email.index('@')] } end 
    end 
end 


bench n, '[email protected]' 
bench n, '[email protected]' 
bench n, '[email protected]' 
bench n, '[email protected]' 
bench n, '[email protected]omain.com' 
bench n, 'a'*254 + '@' + 'b'*253 # rfc limits 
bench n, 'a'*1000 + '@' + 'b'*1000 # for other string processing 

Resultados: 1.9.3p484

[email protected] 1000000 times 
     user  system  total  real 
split  0.405000 0.000000 0.405000 ( 0.410023) 
partition 0.375000 0.000000 0.375000 ( 0.368021) 
slice reg 0.359000 0.000000 0.359000 ( 0.357020) 
slice ind 0.312000 0.000000 0.312000 ( 0.309018) 

[email protected] 1000000 times 
     user  system  total  real 
split  0.421000 0.000000 0.421000 ( 0.432025) 
partition 0.374000 0.000000 0.374000 ( 0.379021) 
slice reg 0.421000 0.000000 0.421000 ( 0.411024) 
slice ind 0.312000 0.000000 0.312000 ( 0.315018) 

[email protected] 1000000 times 
     user  system  total  real 
split  0.593000 0.000000 0.593000 ( 0.589034) 
partition 0.531000 0.000000 0.531000 ( 0.529030) 
slice reg 0.764000 0.000000 0.764000 ( 0.771044) 
slice ind 0.484000 0.000000 0.484000 ( 0.478027) 

[email protected] 1000000 times 
     user  system  total  real 
split  0.483000 0.000000 0.483000 ( 0.481028) 
partition 0.390000 0.016000 0.406000 ( 0.404023) 
slice reg 0.406000 0.000000 0.406000 ( 0.411024) 
slice ind 0.312000 0.000000 0.312000 ( 0.344020) 

[email protected]omain.com 1000000 times 
     user  system  total  real 
split  0.639000 0.000000 0.639000 ( 0.646037) 
partition 0.609000 0.000000 0.609000 ( 0.596034) 
slice reg 0.764000 0.000000 0.764000 ( 0.773044) 
slice ind 0.499000 0.000000 0.499000 ( 0.491028) 

a<254>@b<253> 1000000 times 
     user  system  total  real 
split  0.952000 0.000000 0.952000 ( 0.960055) 
partition 0.733000 0.000000 0.733000 ( 0.731042) 
slice reg 3.432000 0.000000 3.432000 ( 3.429196) 
slice ind 0.624000 0.000000 0.624000 ( 0.625036) 

a<1000>@b<1000> 1000000 times 
     user  system  total  real 
split  1.888000 0.000000 1.888000 ( 1.892108) 
partition 1.170000 0.016000 1.186000 ( 1.188068) 
slice reg 12.885000 0.000000 12.885000 (12.914739) 
slice ind 1.108000 0.000000 1.108000 ( 1.097063) 

2.1.3p242 sostiene sobre las mismas diferencias%, pero es alrededor de 10-30% más rápido en todo, excepto en la división de expresiones regulares en los que se ralentiza aún más .

Cuestiones relacionadas