2008-10-15 10 views
7

Situación:capturar el contenido de una expresión regular y eliminarlos, eficiente

  • texto: una cadena
  • R: una expresión regular que coincide con parte de la cadena. Esto puede ser costoso de calcular.

Quiero eliminar las coincidencias R del texto y ver qué contienen realmente. Actualmente, hago esto como:

import re 
ab_re = re.compile("[ab]") 
text="abcdedfe falijbijie bbbb laifsjelifjl" 
ab_re.findall(text) 
# ['a', 'b', 'a', 'b', 'b', 'b', 'b', 'b', 'a'] 
ab_re.sub('',text) 
# 'cdedfe flijijie lifsjelifjl' 

Esto ejecuta la regex dos veces, cerca como puedo decir. ¿Hay alguna técnica para hacerlo todo al pasar, quizás usando re.split? Parece que con las soluciones basadas en división tendré que hacer la expresión regular al menos dos veces también.

+0

Al menos 3 buenos conjuntos de respuestas. El mío es rápido y simple, usando re.split(). Deestan expone toda la información sobre los partidos utilizando finditer(), y los shows de Jon Cage de que las funciones se pueden usar en re.sub, que es una habilidad inteligente. –

Respuesta

4
import re 

r = re.compile("[ab]") 
text = "abcdedfe falijbijie bbbb laifsjelifjl" 

matches = [] 
replaced = [] 
pos = 0 
for m in r.finditer(text): 
    matches.append(m.group(0)) 
    replaced.append(text[pos:m.start()]) 
    pos = m.end() 
replaced.append(text[pos:]) 

print matches 
print ''.join(replaced) 

Salidas:

['a', 'b', 'a', 'b', 'b', 'b', 'b', 'b', 'a'] 
cdedfe flijijie lifsjelifjl 
+0

Puede usar una lista en lugar de StringIO, y úsela al final, si desea que sea simple. – Tomalak

+0

Tomalak: Sí, eso sería más simple. Un poco de perfil de prueba muestra que en realidad también es más rápido, al menos en mi entrada de prueba. – Deestan

+0

Realmente no me sorprende. Estaba escribiendo esencialmente lo mismo, acabas de ser más rápido.;-) – Tomalak

0

Puede usar división con captura de parantheses. Si lo hace, entonces el texto de todos los grupos en el patrón también se devuelve como parte de la lista resultante (desde python doc).

Así que el código sería

import re 
ab_re = re.compile("([ab])") 
text="abcdedfe falijbijie bbbb laifsjelifjl" 
matches = ab_re.split(text) 
# matches = ['', 'a', '', 'b', 'cdedfe f', 'a', 'lij', 'b', 'ijie ', 'b', '', 'b', '', 'b', '', 'b', ' l', 'a', 'ifsjelifjl'] 

# now extract the matches 
Rmatches = [] 
remaining = [] 
for i in range(1, len(matches), 2): 
    Rmatches.append(matches[i]) 
# Rmatches = ['a', 'b', 'a', 'b', 'b', 'b', 'b', 'b', 'a'] 

for i in range(0, len(matches), 2): 
    remaining.append(matches[i]) 
remainingtext = ''.join(remaining) 
# remainingtext = 'cdedfe flijijie lifsjelifjl' 
+0

Todo el código "if text == a" aquí implementa la expresión regular por segunda vez. Si la expresión regular era simple como [ab], entonces toda esta pregunta sería discutible. :) Buen esfuerzo, y estimula mi pensamiento un poco, en soluciones de filtrado. –

+0

Sí, lo solucionó al notar que las coincidencias tienen una coincidencia alternativa y texto descartado, incluidas las cadenas vacías, por lo que la solución anterior es más simple y la expresión regular solo se ejecuta una vez :) –

+0

Lo suficiente. Sin embargo, rebanar es más simple :) ¡También tuve la misma idea sobre la coincidencia de tokens de alternancia! Gracias por la pista. –

3

Mi respuesta revisada, utilizando re.split(), que hace las cosas de un solo pase de expresiones regulares:

import re 
text="abcdedfe falijbijie bbbb laifsjelifjl" 
ab_re = re.compile("([ab])") 
tokens = ab_re.split(text) 
non_matches = tokens[0::2] 
matches = tokens[1::2] 

(edit: aquí es una versión de función completa)

def split_matches(text,compiled_re): 
    ''' given a compiled re, split a text 
    into matching and nonmatching sections 
    returns m, n_m, two lists 
    ''' 
    tokens = compiled_re.split(text) 
    matches = tokens[1::2] 
    non_matches = tokens[0::2] 
    return matches,non_matches 

m,nm = split_matches(text,ab_re) 
''.join(nm) # equivalent to ab_re.sub('',text) 
+0

Tenga en cuenta que el compilado debe ser una "captura de re" con parens en todo el lío, o esto no funcionará correctamente. –

+0

Hmm? Funciona para mí sin paréntesis. – Deestan

4

Qué tal esto:

import re 

text = "abcdedfe falijbijie bbbb laifsjelifjl" 
matches = [] 

ab_re = re.compile("[ab]") 

def verboseTest(m): 
    matches.append(m.group(0)) 
    return '' 

textWithoutMatches = ab_re.sub(verboseTest, text) 

print matches 
# ['a', 'b', 'a', 'b', 'b', 'b', 'b', 'b', 'a'] 
print textWithoutMatches 
# cdedfe flijijie lifsjelifjl 

El argumento '' repl de la función re.sub puede ser una función para que pueda informe o guarde las coincidencias desde allí y cualquiera que sea la función que devuelva es lo que 'sub' sustituirá.

¡La función podría modificarse fácilmente para hacer mucho más también! Consulte the re module documentation en docs.python.org para obtener más información sobre qué más es posible.

+0

Esa es una solución muy inteligente. No sabía que podría usar funciones como primer argumento para la función secundaria. –

+0

Gracias, estaba bastante contento con su simplicidad cuando me di cuenta de que podías llamar a una función :-) –

Cuestiones relacionadas