2008-11-06 12 views
9

Ok, así que tienen esta expresión regular:¿Puedo optimizar este teléfono-regex?

(|^|>)(((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{2})(-)?()?)?)([0-9]{7}))|((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{3})(-)?()?)?)([0-9]{6}))|((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{1})(-)?()?)?)([0-9]{8})))(|$|<) 

Se da formato a los números de teléfono holandeses y belgas (sólo quiero los de ahí el 31 y 32 como código de país).

No es muy divertido de descifrar, pero como puede ver, también se ha duplicado mucho. pero ahora no lo maneja con mucha precisión

Todos los siguientes números de teléfono con formato europeos se aceptan

00312
0031223234567 
0031612345678 
+31(0)20-1234567 
+31(0)223-234567 
+31(0)6-12345678 
020-1234567 
0223-234567 
06-12345678 
02
0223234567 
0612345678 

y los siguientes formateados falsas no son

06-1234567 (mobile phone number in the Netherlands should have 8 numbers after 06) 
0223-1234567 (area code with home phone) 

a diferencia de este, que es bueno .

020-1234567 (area code with 3 numbers has 7 numbers for the phone as opposed to a 4 number area code which can only have 6 numbers for phone number) 

Como se puede ver que es el carácter '-' que hace que sea un poco más difícil, pero lo necesito ahí porque es una parte del formato general utilizado por la gente, y quiero ser capaz de analizar las todas.

Ahora es mi pregunta ... ¿ves una forma de simplificar esta expresión regular (o incluso mejorarla si ves un error en ella), manteniendo las mismas reglas?

Puede probarlo en regextester.com

(La '(|^|>)' es comprobar si se encuentra al principio de una palabra con la posibilidad que está precedida por cualquiera de una nueva línea o un " > '. Busco los números de teléfono en páginas HTML.)

+0

Mi primera pregunta es: ¿realmente necesitas TODAS esas capturas? ¿No puedes agarrar las partes importantes y reformatearlas? ¿Cuáles son las partes pertinentes? – Axeman

+0

no busco los números de teléfono en un montón de textos. No sé dónde está el número y de qué manera convencional está formateado. después de que lo encontré, básicamente ya no lo necesito – youri

Respuesta

12

Primera observación: leer la expresión regular es una pesadilla. Grita por el modo Perl/x.

Segunda observación: hay muchos, muchos, y muchos paréntesis de captura en la expresión (42 si cuento correctamente, y 42 es, por supuesto, "La respuesta a la vida, el universo y todo" - ver Douglas Adams "Guía del Hitchiker para la Galaxia" si necesita que se lo explique).

Bill the Lizard señala que utiliza '(-)?()?' varias veces. No hay ninguna ventaja obvia en comparación con '-? ?' o posiblemente '[- ]?', a menos que realmente desee capturar la puntuación real por separado (pero hay tantos paréntesis de captura que resuelven qué elementos '$ n' se usarán difícil).

Por lo tanto, vamos a intentar editar una copia de su sola línea:

(|^|>) 
(
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{2})(-)?()?)?)([0-9]{7})) | 
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{3})(-)?()?)?)([0-9]{6})) | 
    ((((((\+|00)(31|32)()?(\(0\))?)|0)([0-9]{1})(-)?()?)?)([0-9]{8})) 
) 
(|$|<) 

OK - ahora podemos ver la estructura regular de su expresión regular.

Aquí hay mucho más análisis posible. Sí, puede haber grandes mejoras en la expresión regular. El primero, obvio, es extraer la parte de prefijo internacional y aplicarla una vez (opcionalmente, o requerir el cero inicial) y luego aplicar las reglas nacionales.

(|^|>) 
(
    (((\+|00)(31|32)()?(\(0\))?)|0) 
    (((([0-9]{2})(-)?()?)?)([0-9]{7})) | 
    (((([0-9]{3})(-)?()?)?)([0-9]{6})) | 
    (((([0-9]{1})(-)?()?)?)([0-9]{8})) 
) 
(|$|<) 

Entonces podemos simplificar la puntuacion como se ha señalado antes, y eliminar algunos paréntesis plausiblemente redundantes, y mejorar el código de país reconocedor:

(|^|>) 
(
    (((\+|00)3[12] ?(\(0\))?)|0) 
    (((([0-9]{2})-? ?)?)[0-9]{7}) | 
    (((([0-9]{3})-? ?)?)[0-9]{6}) | 
    (((([0-9]{1})-? ?)?)[0-9]{8}) 
) 
(|$|<) 

Podemos observar que la expresión regular no hace cumplir las normas sobre Códigos de teléfonos móviles (por lo tanto, no insiste en que "06" vaya seguido de 8 dígitos, por ejemplo). También parece permitir que el código de intercambio de 1, 2 o 3 dígitos sea opcional, incluso con un prefijo internacional, probablemente no sea lo que tenía en mente, y corregir eso elimina algunos paréntesis más. Podemos eliminar aún más paréntesis, después de eso, lo que lleva a:

(|^|>) 
(
    (((\+|00)3[12] ?(\(0\))?)|0) # International prefix or leading zero 
    ([0-9]{2}-? ?[0-9]{7}) |  # xx-xxxxxxx 
    ([0-9]{3}-? ?[0-9]{6}) |  # xxx-xxxxxx 
    ([0-9]{1}-? ?[0-9]{8})   # x-xxxxxxxx 
) 
(|$|<) 

y se puede trabajar a cabo optimizaciones adicionales de aquí, yo espero.

+1

gracias rompí por mi cuenta para ver si podía lograr esto, pero debo haber hecho algo mal ... gracias esto es realmente útil – youri

+1

golpe muy viejo, pero acabo de ver la parte de 42 ... eso es nice: P cheers mate: P – youri

+0

¿Cómo se puede obtener trabajando con PHP y preg_replace? – Sanne

8

¡Buen Dios Todopoderoso, qué desastre! :) Si tiene reglas semánticas o comerciales de alto nivel (como las que describe al hablar de números europeos, números en los Países Bajos, etc.) probablemente sería mejor que dividiera esa prueba única de expresiones regulares en varias pruebas individuales de expresiones regulares, uno para cada una de sus reglas de alto nivel.

if number =~ /...../ # Dutch mobiles 
    # ... 
elsif number =~ /..../ # Belgian landlines 
    # ... 
# etc. 
end 

Va a ser un poco más fácil de leer y mantener y cambiar de esa manera.

+0

Y ordene sus pruebas por lo más probable que coincidan (suponiendo que conozca los datos demográficos lo suficientemente bien). – tvanfosson

+0

@tvanfosson: Claro; convenido. – Pistos

+0

que no pensé en eso: P gracias :) – youri

3

Dividirlo en varias expresiones. Por ejemplo (pseudo-código) ...

phone_no_patterns = [ 
    /[0-9]{13}/, # 00312
    /+(31|32)\(0\)\d{2}-\d{7}/ # +31(0)20-1234567 
    # ..etc.. 
] 
def check_number(num): 
    for pattern in phone_no_patterns: 
     if num matches pattern: 
      return match.groups 

A continuación, sólo un bucle sobre cada patrón, comprobar si cada uno coincide ..

Dividir los patrones Hace que su fácil de solucionar números específicos que están causando problemas (que sería horrible, con la sola expresión regular monolítica)

2

no es una optimización, pero utilizar

(-)?()? 

tres veces en su expresión regular.Esto hará que el partido en los números de teléfono como estos

+31(0)6-12345678 
+31(0)6 12345678 

sino también coincidirá con los números que contienen un guión seguido de un espacio, como

+31(0)6- 12345678 

Puede reemplazar

(-)?()? 

con

(-|)? 

para que coincida con un guión o un espacio.

+0

mejor aún '(-)?' –

+0

Eso es mejor. Tu solución guarda un personaje. Me estaba ahorrando escribiendo a máquina. :) –

+0

no noté que lo hice gracias – youri

3

(31 | 32) se ve mal. Al hacer coincidir 32, el motor de expresiones regulares primero intentará hacer coincidir 31 (2 caracteres), fallar y retroceder dos caracteres para que coincida con 31. Es más eficiente hacer coincidir primero 3 (un carácter), probar 1 (fallar), retroceder un carácter y partido 2.

Por supuesto, su expresión regular falla en 0800- números; no tienen 10 dígitos

+0

no quiero 0800 números, pero la otra parte de tu comentario fue útil gracias. – youri