2016-12-27 12 views
5

Я хотел бы очистить ввод, который был записан с моей клавиатуры с помощью python и regex. Особенно, когда задняя часть была использована для исправления ошибки.Совпадение с тем же числом повторений символа, что и повторение захваченной группы

Пример 1:

[in]: 'Helloo<BckSp> world' 
[out]: 'Hello world' 

Это может быть сделано с

re.sub(r'.<BckSp>', '', 'Helloo<BckSp> world') 

Пример 2:
Однако, когда у меня есть несколько забоя, я не знаю, как удалить точно такое же количество до:

[in]: 'Helllo<BckSp><BckSp>o world' 
[out]: 'Hello world' 

(Здесь I w ant удалить «l» и «o» перед двумя обратными окнами).

Я могу просто использовать re.sub(r'[^>]<BckSp>', '', line) несколько раз, пока нет <BckSp>, но я хотел бы найти более элегантное/быстрое решение.

Кто-нибудь знает, как это сделать?

+1

Я думаю, что вы не можете рассчитывать с регулярным выражением и просто перекручивания через регулярное выражение, как предложено это лучший способ – Fallenhero

+1

Пользуется регулярным выражением требования (т.е. вы изучаете регулярные выражения), или просто ваше предложенное решение? –

+0

Да Я пытаюсь использовать регулярное выражение, чтобы узнать, потому что я еще не знаком с ним. –

ответ

1

Поскольку нет никакой поддержки рекурсии/подпрограмм, без каких-либо групп атомов/притяжательных кванторы в Python re, вы можете удалить эти символы следуют с забоем в цикле:

import re 
s = "Helllo\b\bo world" 
r = re.compile("^\b+|[^\b]\b") 
while r.search(s): 
    s = r.sub("", s) 
print(s) 

Смотрите Python demo

Образец "^\b+|[^\b]\b" найдет 1+ символы backspace в начале строки (с ^\b+) и [^\b]\b найдет все неперекрывающиеся вхождения любого символа, кроме заднего пространства, за которым следует обратное пространство.

же подход в случае забой выражаются в виде некоторой enitity/тэг как буквальный <BckSp>:

import re 
s = "Helllo<BckSp><BckSp>o world" 
r = re.compile("^(?:<BckSp>)+|.<BckSp>", flags=re.S) 
while r.search(s): 
    s = r.sub("", s) 
print(s) 

См another Python demo

+2

OP уже рассмотрел цикл и ищет лучшее решение. –

2

Это выглядит как Python не поддерживает рекурсивное регулярное выражение. Если вы можете использовать другой язык, вы можете попробовать это:

.(?R)?<BckSp> 

См: https://regex101.com/r/OirPNn/1

+0

Ну, можно установить модуль регулярного выражения PyPi и использовать этот подход в Python. –

2

Это не очень эффективно, но вы можете сделать это с помощью модуля Re:

(?:[^<](?=[^<]*((?=(\1?))\2<BckSp>)))+\1 

demo

Таким образом, вам не нужно учитывать, шаблон использует повторение.

(?: 
    [^<] # a character to remove 
    (?= # lookahead to reach the corresponding <BckSp> 
     [^<]* # skip characters until the first <BckSp> 
     ( # capture group 1: contains the <BckSp>s 
      (?=(\1?))\2 # emulate an atomic group in place of \1?+ 
         # The idea is to add the <BcKSp>s already matched in the 
         # previous repetitions if any to be sure that the following 
         # <BckSp> isn't already associated with a character 
      <BckSp> # corresponding <BckSp> 
     ) 
    ) 
)+ # each time the group is repeated, the capture group 1 is growing with a new <BckSp> 

\1 # matches all the consecutive <BckSp> and ensures that there's no more character 
    # between the last character to remove and the first <BckSp> 

Вы можете сделать то же самое с модулем регулярных выражений, но на этот раз вам не нужно подражать притяжательные квантор:

(?:[^<](?=[^<]*(\1?+<BckSp>)))+\1 

demo

Но с модулем регулярных выражений, вам может также использовать рекурсию (как заметил @Fallenhero):

[^<](?R)?<BckSp> 

demo

+0

Не может голосовать за это без каких-либо объяснений, кроме демонстрации. –

1

В случае маркер один символ вы могли бы просто использовать стек, который даст вам результат в один проход:

s = "Helllo\b\bo world" 
res = [] 

for c in s: 
    if c == '\b': 
     if res: 
      del res[-1] 
    else: 
     res.append(c) 

print(''.join(res)) # Hello world 

В случае маркер в буквальном смысле '<BckSp>' или какой-либо другой строки с длиной больше 1 вы можете использовать replace, чтобы заменить его на '\b' и используйте вышеуказанное решение. Это работает, только если вы знаете, что '\b' не встречается на входе. Если вы не можете назначить замещающий символ, который вы могли бы использовать split и обрабатывать результаты:

s = 'Helllo<BckSp><BckSp>o world' 
res = [] 

for part in s.split('<BckSp>'): 
    if res: 
     del res[-1] 
    res.extend(part) 

print(''.join(res)) # Hello world 
+1

Простой и эффективный, но не OP хочет изучить регулярное выражение. –

+0

Хороший подход. У вас есть обходное решение, если маркер «'? Возможно, замена его на '\ b' будет самой простой ... –

+1

@LouisM Replace будет проще всего, если вы знаете символ, который не встречается во входе. Я добавил альтернативное решение для случая, когда вы не можете назначить ни одного символа для использования в качестве замены. – niemmi

1

Слегка многословным, но вы можете использовать этот lambda function рассчитывать # из <BckSp> возникновения и использовать подстроку процедуру, чтобы получить конечный результат.

>>> bk = '<BckSp>' 

>>> s = 'Helllo<BckSp><BckSp>o world' 
>>> print re.sub(r'(.*?)((?:' + bk + ')+)', lambda x: x.group(1)[0:len(x.group(1)) - len(x.group(2))/len(bk)], s) 
Hello world 

>>> s = 'Helloo<BckSp> world' 
>>> print re.sub(r'(.*?)((?:' + bk + ')+)', lambda x: x.group(1)[0:len(x.group(1)) - len(x.group(2))/len(bk)], s) 
Hello world 

>>> s = 'Helloo<BckSp> worl<BckSp>d' 
>>> print re.sub(r'(.*?)((?:' + bk + ')+)', lambda x: x.group(1)[0:len(x.group(1)) - len(x.group(2))/len(bk)], s) 
Hello word 

>>> s = 'Helllo<BckSp><BckSp>o world<BckSp><BckSp>k' 
>>> print re.sub(r'(.*?)((?:' + bk + ')+)', lambda x: x.group(1)[0:len(x.group(1)) - len(x.group(2))/len(bk)], s) 
Hello work