Удаление данных между двойными скобками с вложенными скобками в python
У меня возникли трудности с этой проблемой. Мне нужно удалить все данные, содержащиеся в скользящих скобках.
Подобно такому:
Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there.
становится:
Hello there.
Вот моя первая попытка (я знаю это ужасно):
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:];
Кажется, что он работает, но заканчивается довольно быстро. Есть ли лучший способ сделать это (надеюсь, с регулярным выражением)?
EDIT: я не был ясен, поэтому приведу еще один пример. Мне нужно разрешить несколько скобок верхнего уровня.
Подобно такому:
Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there {{my }} friend.
становится:
Hello there friend.
Ответы
Ответ 1
Это решение на основе регулярного выражения/генератора, которое работает с любым количеством фигурных скобок. Эта проблема не нуждается в фактическом стеке, потому что задействован только один тип (ну, пара) маркера. level
заполняет роль, которую стек заполняет более сложный парсер.
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)))
Более общие моменты... группа захвата в регулярном выражении - это то, что заставляет фигурные скобки появляться на выходе re.split
, иначе вы получите только материал между ними. Там также есть поддержка несогласованных брекетов. Для строгого парсера это должно вызвать исключение, так как должно заканчиваться конец строки с уровнем > 0. Для свободного анализатора веб-браузера, возможно, вы захотите отобразить эти }}
в качестве вывода...
Ответ 2
Здесь вы можете использовать pyparsing
module. Решение, основанное на этом ответе:
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)))
Печать
Hello there friend.
Следующее будет работать только в том случае, если имеется только одна пара фигур верхнего уровня.
Если вы хотите удалить все внутри двойных фигурных скобок с помощью самих скобок:
>>> import re
>>>
>>> s = "Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there."
>>> re.sub(r"\{\{.*\}\} ", "", s)
'Hello there.'
\{\{.*\}\}
будет соответствовать двойным фигурным скобкам, за которыми следуют любые символы любое количество раз (намеренно оставил его "жадным" ), а затем двойные фигурные скобки и пробел.
Ответ 3
Попробуйте использовать следующий код:
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)
Ответ 4
Проблема в том, что вам придется иметь дело с вложенной структурой, что означает, что регулярное выражение может быть недостаточно. Однако простой синтаксический анализатор с памятью уровня глубины может прийти на помощь - его очень просто написать, просто сохраните
уровень глубины в переменную.
Я просто разместил более питонический способ написания решения здесь, что может быть хорошей ссылкой для вас.
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.
Ответ 5
Для хорошей меры - еще одно решение. Он начинается с поиска и замены самых левых сокровенных фигурных скобок и работает наружу, вправо. Заботится о нескольких скобках верхнего уровня.
import re
def remove_braces(s):
pattern = r'\{\{(?:[^{]|\{[^{])*?\}\}'
while re.search(pattern, s):
s = re.sub(pattern, '', s)
return s
Не самый эффективный, но короткий.
>>> remove_braces('Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there {{my }} friend.')
'Hello there friend.'
Ответ 6
Этот вопрос делает забаву. Вот моя попытка:
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 .'
Ответ 7
С Пакет Python regex может использовать рекурсивное регулярное выражение .
{{(?>[^}{]+|(?0))*}} ?
Или другой вариант (требуется немного больше).
{{(?>[^}{]*(?R)?)*}} ?
Вставляется (?0)
или (?R)
шаблон. Используйте с regex.sub
>>> import regex
>>> str = 'Hello {{world of the {{ crazy}} {{need {{ be}}}} sea }} there.'
>>> regex.sub(r'(?V1){{(?>[^}{]+|(?0))*}} ?', '', str)
(?V1)
Версия 1 ведет себя как Perl. Не могу проверить это, вам нужно попробовать:)