2016-02-25 32 views
13

Ho qualche difficoltà con questo problema. Devo rimuovere tutti i dati contenuti in parentesi graffe.Rimozione dei dati tra parentesi graffe doppie con parentesi quadre nidificate in pitone

Come ad esempio:

Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there. 

diventa:

Hello there. 

Ecco il mio primo tentativo (lo so che è terribile):

while 1: 
    firstStartBracket = text.find('{{') 
    if (firstStartBracket == -1): 
     break; 
    firstEndBracket = text.find('}}') 
    if (firstEndBracket == -1): 
     break; 
    secondStartBracket = text.find('{{',firstStartBracket+2); 
    lastEndBracket = firstEndBracket; 
    if (secondStartBracket == -1 or secondStartBracket > firstEndBracket): 
     text = text[:firstStartBracket] + text[lastEndBracket+2:]; 
     continue; 
    innerBrackets = 2; 
    position = secondStartBracket; 
    while innerBrackets: 
     print innerBrackets; 
     #everytime we find a next start bracket before the ending add 1 to inner brackets else remove 1 
     nextEndBracket = text.find('}}',position+2); 
     nextStartBracket = text.find('{{',position+2); 
     if (nextStartBracket != -1 and nextStartBracket < nextEndBracket): 
      innerBrackets += 1; 
      position = nextStartBracket; 
      # print text[position-2:position+4]; 
     else: 
      innerBrackets -= 1; 
      position = nextEndBracket; 
      # print text[position-2:position+4]; 
      # print nextStartBracket 
      # print lastEndBracket 
      lastEndBracket = nextEndBracket; 
     print 'pos',position; 
    text = text[:firstStartBracket] + text[lastEndBracket+2:]; 

Sembra funzionare, ma esaurisce la memoria abbastanza veloce. C'è un modo migliore per farlo (eventualmente con espressioni regolari)?

MODIFICA: non ero chiaro quindi darò un altro esempio. Devo consentire più staffe di primo livello.

Come ad esempio:

Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there {{my }} friend. 

diventa:

Hello there friend. 
+0

quindi in pratica vuoi eliminare tutte le cose dentro {}, giusto? – GLHF

+0

Tuttavia, come punto, se non si desidera utilizzare 'regex' utilizzare lo stack per contare il numero di parentesi aperte e quelle chiuse, è più semplice del codice di esempio qui. – Arman

+0

@GLHF no deve essere dentro {{}} e non solo {} – thewormsterror

risposta

4

Si tratta di una soluzione basata su regex/generatore che funziona con qualsiasi numero di parentesi graffe. Questo problema non ha bisogno di uno stack effettivo perché c'è solo 1 tipo (bene, coppia) di token coinvolti. Lo level riempie il ruolo che uno stack riempie in un parser più complesso.

import re 

def _parts_outside_braces(text): 
    level = 0 
    for part in re.split(r'(\{\{|\}\})', text): 
     if part == '{{': 
      level += 1 
     elif part == '}}': 
      level = level - 1 if level else 0 
     elif level == 0: 
      yield part 

x = 'Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there. {{ second set {{ of }} braces }}' 
print(''.join(_parts_outside_braces(x))) 

Più punti generali ... il gruppo di cattura nella espressione regolare è ciò che rende le parentesi appaiono nell'output di re.split, altrimenti si ottiene solo la roba in mezzo. C'è anche qualche supporto per le parentesi graffe non corrispondenti. Per un parser rigoroso, dovrebbe sollevare un'eccezione, come dovrebbe essere eseguito alla fine della stringa con livello> 0. Per un parser loose, browser web, forse vorresti visualizzare quelli }} come output ...

+0

Ho appena provato, funziona come un fascino :) Grazie mille. – thewormsterror

+0

Questa è in realtà la soluzione più veloce finora. ~ 15 μs – Moritz

4

È possibile utilizzare pyparsing module qui. Soluzione basata su this answer:

from pyparsing import nestedExpr 


s = "Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there {{my }} friend." 

expr = nestedExpr('{{', '}}') 
result = expr.parseString("{{" + s + "}}").asList()[0] 
print(" ".join(item for item in result if not isinstance(item, list))) 

stampe:

Hello there friend. 

Questa situazione funziona solo se v'è solo una coppia di primo livello di parentesi.

Se si desidera rimuovere tutto dentro le parentesi doppie graffe con le parentesi stesse:

>>> import re 
>>> 
>>> s = "Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there." 
>>> re.sub(r"\{\{.*\}\} ", "", s) 
'Hello there.' 

\{\{.*\}\} sarebbe partita parentesi graffe doppie seguita da qualsiasi carattere un numero illimitato di volte (volutamente lasciata "greedy") seguito da doppie parentesi graffe e uno spazio.

+1

Funziona solo se si presume che non ci possa essere più di una coppia di parentesi di livello superiore. –

+0

@JasonS buon punto! – alecxe

+0

@alecxe Mi dispiace non essere stato chiaro con la mia domanda iniziale. – thewormsterror

1

provare il seguente codice:

import re 

s = 'Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there' 
m = re.search('(.*?) {.*}(.*)',s) 
result = m.group(1) + m.group(2) 
print(result) 
+0

Funziona solo se si presume che non ci possano essere più di una coppia di parentesi di livello superiore. –

1

Il problema è che dovresti avere a che fare con la struttura annidata, il che significa che l'espressione regolare potrebbe non essere sufficiente.Tuttavia, un parser semplice con una memoria di livello di profondità può venire in soccorso - è molto semplice da scrivere, basta memorizzare il livello di profondità in una variabile.

Ho appena pubblicato un modo più pitioso di scrivere la soluzione qui, che potrebbe essere un buon riferimento per voi.

import re 

def rem_bra(inp): 
    i = 0 
    lvl = 0 
    chars = [] 
    while i < len(inp): 
     if inp[i:i+2] == '{{': 
      lvl += 1 
      i += 1 
     elif inp[i:i+2] == '}}': 
      lvl -= 1 
      i += 1 
     else: 
      if lvl < 1: 
       chars.append(inp[i]) 
     i += 1 
    result = ''.join(chars) 

    # If you need no more contigious spaces, add this line: 
    result = re.sub(r'\s\s+', r' ', result) 

    return result 


inp = "Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there." 

print(rem_bra(inp)) 
>>> Hello there. 
1

Per buona misura, ancora un'altra soluzione. Inizia trovando e sostituendo le parentesi graffe più a sinistra e procedendo verso l'esterno, verso destra. Si prende cura di più tutori di primo livello.

import re 

def remove_braces(s): 
    pattern = r'\{\{(?:[^{]|\{[^{])*?\}\}' 
    while re.search(pattern, s): 
     s = re.sub(pattern, '', s) 
    return s 

Non il più efficiente, ma breve.

>>> remove_braces('Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there {{my }} friend.') 
'Hello there friend.' 
+0

stai scherzando? Il tuo è altamente efficiente. Prende circa 90 ns per un ciclo rispetto a 10 - 25 microsecondi per la maggior parte delle altre soluzioni. – Moritz

+0

misurato sbagliato. prende 25 μs – Moritz

+0

@Moritz ho indovinato così :) Fa ricerca/abbina la stringa più volte, mentre la soluzione di Jason S, diciamo, lo fa solo una volta. Ma mi piace poco. – gil

1

Questa domanda è divertente. Qui è il mio tentativo:

import re 

def find_str(string): 

    flag = 0 

    for index,item in enumerate(string): 

     if item == '{': 
      flag += 1 

     if item == '}': 
      flag -= 1 

     if flag == 0: 
      yield index 

s = 'Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there {{my }} friend.' 

index = list(find_str(s)) 

l = [s[i] for i in index] 

s = ' '.join(l) 

re.sub('}\s+','',s) 

'H e l l o t h e r e f r i e n d .'

0

Con Python regex package potrebbe utilizzare un recursive regex.

 
{{(?>[^}{]+|(?0))*}} ? 

O un'altra variante (richiede un po 'più passo).

 
{{(?>[^}{]*(?R)?)*}} ? 

A (?0) o (?R) modello viene incollato. Usare con regex.sub

>>> import regex 
>>> str = 'Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there.' 
>>> regex.sub(r'(?V1){{(?>[^}{]+|(?0))*}} ?', '', str) 

(?V1) Versione 1 si comporta come Perl. Non posso testarlo, devi provare :)

Problemi correlati