2016-02-25 12 views
13

Mam problem z tym problemem. Muszę usunąć wszystkie dane zawarte w nawiasach kwadratowych.Usuwanie danych między podwójnymi nawiasami kwadratowymi a zagnieżdżonymi podpunkami pod pytonem

jak takie:

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

Staje:

Hello there. 

Oto moja pierwsza próba (Wiem, że to straszne):

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:]; 

Wydaje się działać, ale zabraknie pamięci dosyć szybko. Czy istnieje lepszy sposób to zrobić (miejmy nadzieję, z regex)?

EDYCJA: Nie było jasne, więc dam inny przykład. Muszę uwzględnić wiele nawiasów najwyższego poziomu.

takie jak:

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

Staje:

Hello there friend. 
+0

więc w zasadzie chcesz usunąć wszystkie rzeczy wewnątrz {}, prawda? – GLHF

+0

Jednak jeśli nie chcesz używać stosu 'regex' do zliczania liczby otwartych nawiasów i zamkniętych, to łatwiejsze niż przykładowy kod tutaj. – Arman

+0

@GLHF nie musi być wewnątrz {{}}, a nie tylko {} – thewormsterror

Odpowiedz

4

Jest to rozwiązanie oparte regex/generator, który współpracuje z dowolną liczbą szelkami. Ten problem nie wymaga rzeczywistego stosu, ponieważ dotyczy on tylko jednego typu (dobrze, pary) tokena. level wypełnia rolę, którą stos mógłby wypełnić bardziej złożony analizator składni.

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))) 

Bardziej ogólne punkty ... grupa przechwytywania w regex co sprawia, że ​​szelki pokazać się na wyjściu re.split, w przeciwnym razie można dostać tylko rzeczy pomiędzy. Istnieje również wsparcie dla niedopasowanych klamr. Dla ścisłego analizatora składni, który powinien podnieść wyjątek, tak jak powinno się kończyć ciągiem znaków z poziomem> 0. Dla luźnego parsera stylu przeglądarki internetowej, być może chciałbyś wyświetlić te }} jako dane wyjściowe ...

+0

Po prostu wypróbowałem, działa jak urok :) Wielkie dzięki. – thewormsterror

+0

Jest to obecnie najszybsze rozwiązanie. ~ 15 μs – Moritz

4

Można użyć pyparsing module tutaj. Rozwiązanie oparte na 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))) 

Wydruki:

Hello there friend. 

Poniższy będzie działać tylko wtedy, gdy istnieje tylko jedna para najwyższego poziomu z szelkami.

Jeśli chcesz usunąć wszystko wewnątrz podwójnych nawiasach klamrowych z samych szelek:

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

\{\{.*\}\} byłoby pasujących podwójne nawiasy klamrowe następnie wszelkich znaków dowolną liczbę razy (celowo go "greedy"), a następnie podwójne nawiasy klamrowe i spacja.

+1

Działa tylko wtedy, gdy założymy, że nie może być więcej niż jedna para najwyższych klamr. –

+0

@JasonS dobry punkt! – alecxe

+0

@alecxe Przykro mi, że nie było jasne z moim pierwotnym pytaniem. – thewormsterror

1

Wypróbuj poniższy kod:

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

Działa tylko wtedy, gdy zakłada się, że nie może być więcej niż jedna para najwyższych klamr. –

1

Problem polega na tym, że musisz uporać się ze strukturą zagnieżdżoną, co oznacza, że ​​wyrażenie regularne może nie wystarczyć.Jednak prosty parser z pamięcią o poziomie głębokości może przyjść na ratunek - bardzo łatwo jest zapisać, wystarczy zapisać poziom głębi w zmiennej.

Po prostu publikuję bardziej pythonic sposób pisania rozwiązania tutaj, które mogą być dobrym punktem odniesienia dla Ciebie.

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

Na dokładkę, jeszcze inne rozwiązanie. Zaczyna się od znalezienia i wymiany najbardziej wysuniętych do lewej klamry i działa na zewnątrz, w prawo. Dba o wiele aparatów najwyższego poziomu.

import re 

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

Nie najbardziej wydajny, ale krótki.

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

żartujesz? Twoja jest bardzo wydajna. Zajmuje około 90 ns dla jednej pętli w porównaniu do 10-25 mikrosekund dla większości innych rozwiązań. – Moritz

+0

zmierzył to źle. zajmuje 25 μs – Moritz

+0

@Moritz, więc zgadłem, że tak :) Wykonuje wyszukiwanie/dopasowywanie ciągów wiele razy, podczas gdy rozwiązanie Jason S, powiedzmy, robi to tylko raz. Ale lubię to krótko. – gil

1

To pytanie sprawia radość. Oto moja próba:

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

Z Python regex package przydałby się recursive regex.

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

Albo inny wariant (wymaga nieco więcej krok).

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

Na (?0) lub (?R) wzór jest wklejony. Skorzystaj z regex.sub

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

(?V1) Wersja 1 zachowuje się jak Perl. Nie mogę tego przetestować, musisz spróbować :)