2011-04-19 11 views
7

que tienen un gran conjunto de texto del mundo real que tengo que tirar de palabras fuera de a la entrada en un comprobador de ortografía. Me gustaría extraer tantas palabras significativas como sea posible sin demasiado ruido. Sé que hay muchos ninjas regex por aquí, así que espero que alguien me pueda ayudar.Extracción de palabras completas

Actualmente estoy extraer todas las secuencias alfabéticas con '[a-z]+'. Esta es una buena aproximación, pero arrastra una gran cantidad de basura con ella.

Idealmente me gustaría un poco de expresiones regulares (no tiene que ser bonita o eficiente) que extrae todas las secuencias alfabéticas delimitadas por separadores de palabras naturales (tales como [/-_,.: ] etc.), e ignora las secuencias alfabéticas con límites ilegales.

Sin embargo, también estaría feliz de simplemente ser capaz de obtener todas las secuencias alfabéticas que no son adyacentes a un número. Así, por ejemplo 'pie21' no sería extraer 'pie', pero 'http://foo.com' extraería ['http', 'foo', 'com'].

me trataron lookahead y lookbehind afirmaciones, sino que se aplicaron por carácter (así por ejemplo re.findall('(?<!\d)[a-z]+(?!\d)', 'pie21') volvería 'pi' cuando quiero que vuelva nada). Traté de envolver la parte alfa como un término ((?:[a-z]+)) pero no ayudó.

más detalle: los datos es una base de datos de correo electrónico, por lo que es sobre todo la llanura Inglés con un número normal, pero a veces no hay cadenas de basura como GIHQ4NWL0S5SCGBDD40ZXE5IDP13TYNEA y AC7A21C0 que me gustaría ignorar por completo. Supongo que cualquier secuencia alfabética con un número es basura.

+0

Mejor uso cadenas sin formato con expresiones regulares. '\ d' funciona, pero otras secuencias de escape fallarán, y esto puede ser difícil de depurar. –

Respuesta

16

Si se restringe a sí mismo a las letras ASCII, a continuación, utilizar (con el conjunto re.I opción)

\b[a-z]+\b 

\b es un ancla de límite de palabra, a juego sólo al principio y al final de "palabras" alfanuméricos. Entonces \b[a-z]+\b coincide con pie, pero no pie21 o 21pie.

Para permitir también otras letras no ASCII, se puede usar algo como esto:

\b[^\W\d_]+\b 

que también permite que los caracteres acentuados, etc. Es posible que necesite para establecer la opción re.UNICODE, especialmente cuando se utilizan Python 2, en para permitir que la taquigrafía \w coincida con letras que no sean ASCII.

[^\W\d_] como una clase de caracteres negada permite cualquier carácter alfanumérico, excepto para los dígitos y subrayados.

+0

Esto suena exactamente como lo que quiero, pero no puedo hacer que los '' b's blandos funcionen. Con 'text' establecido como una oración normal,' re.findall ('\ b [a-z] + \ b', text, re.I) 'no devuelve nada. No importa lo que ponga en los corchetes (o usando 'search' o' match') tampoco parece ayudar. Usar '\ B' me da algunos resultados, pero quita el primer y el último carácter de cada palabra. Por perezoso que parezca, estoy demasiado cansado para tomar un nuevo concepto en este momento; ¿Alguna posibilidad de que sepas por qué no está funcionando? ¿O que puedes publicar un ejemplo literal de cómo lo usarías en este caso? – orlade

+4

Eso es * exactamente * por lo que escribí mi comentario a su pregunta. Si no usa cadenas sin formato ('r" \ b [a-z] \ b "'), el '\ b' se interpretará como un carácter de retroceso. –

+0

Ooooooooooooh, eso es lo que querías decir :). Lo siento, ahora son las 5:30 am aquí y nunca iba a hacer esa conexión. ¡Simplemente agregue el r y funciona como un regalo! Gracias Señor. – orlade

3

¿Está familiarizado con word boundaries? (\b). Puede extraer la palabra de usar el \b alrededor de la secuencia y que coincide con el alfabeto dentro:

\b([a-zA-Z]+)\b 

Por ejemplo, esto se agarra palabras completas, pero parar en fichas como guiones, puntos, punto y coma, etc.

Puede \b las secuencias, y otros, a lo largo de la EDITARpython manual

Además, si lo que busca es acerca de una serie siguiente o anterior al partido, se puede utilizar un preanálisis negativo/detrás:

(?!\d) # negative look-ahead for numbers 
(?<!\d) # negative look-behind for numbers 
+0

Según la respuesta de Tim, '\ b' suena como lo que quiero pero no está jugando bien. ¿Algunas ideas? Probé el lookahead y el lookbehinds antes, pero parecen unir todos los personajes hasta el personaje adyacente a un número, por lo que no ignoran por completo las palabras con números. También se queja de lookaheads que necesitan patrones de ancho fijo con esos + s allí. – orlade

+0

@ Pie21: Entonces solo usa una coincidencia de un solo dígito. No nos importa cuántos números lo publiquen o lo precedan, solo que hay un dígito. [ejemplo] (http://re.dabase.com/webre.py?input=pie21+21pie+21pie21+pie®ex=\b%28%3F%3C!\d%29%28 [a-zA-Z] % 2B% 29% 28% 3F! \ D% 29 \ b) –

+0

Tengo este trabajo [re.findall (r "\ b ([a-zA-Z] +) \ b", contenido, re.I) ] pero no parece desviarse hacia adelante y hacia atrás.Aquí hay algunas palabras que salieron: '[endif]', '$', '8', '/ small', '/ li' – Bill

2

¿Qué hay de:

import re 
yourString="pie 42 http://foo.com GIHQ4NWL0S5SCGBDD40ZXE5IDP13TYNEA pie42" 
filter (lambda x:re.match("^[a-zA-Z]+$",x),[x for x in set(re.split("[\s:/,.:]",yourString))]) 

Tenga en cuenta que:

  • dividida explota su cadena en potenciales candidatos => devuelve una lista de "posibles palabras"
  • conjunto hace que el filtrado de unicidad => transforma la lista en conjunto, eliminando así las entradas que aparecen más de una vez. Este paso no es obligatorio.
  • filtro
  • reduce el número de candidatos: toma una lista, aplica una función de prueba a cada elemento y devuelve una lista del elemento que sucede a la prueba. En nuestro caso, la función de prueba es "anónimo"
  • lambda: función anónima, teniendo un artículo y de comprobar si se trata de una palabra (letras superiores o inferiores solamente)

EDIT: añadió algunas explicaciones

+6

¡Mis ojos! ¡Esto casi se parece a Perl! Oh, la humanidad ... –

+0

es por eso que es hermoso – Bruce

+0

feo como es, funciona! ¡Aclamaciones! Sin embargo, ¿puedo pedir un favor más? Ya que no hablo lambda O filtro, ¿hay alguna manera de hacer ese tipo de cosas con 're.finditer()'? Necesito hacer un seguimiento de los índices de inicio y final de cada partido en el texto también. – orlade

0

código de ejemplo

print re.search(ur'(?u)ривет\b', ur'Привет') 
print re.search(ur'(?u)\bривет\b', ur'Привет') 

o

s = ur"abcd ААБВ" 
import re 
rx1 = re.compile(ur"(?u)АБВ") 
rx2 = re.compile(ur"(?u)АБВ\b") 
rx3 = re.compile(ur"(?u)\bАБВ\b") 
print rx1.findall(s) 
print rx2.findall(s) 
print rx3.findall(s) 
Cuestiones relacionadas