игнорируя скобки и первое вхождение с re.sub - PullRequest
0 голосов
/ 03 мая 2020

Я работаю над шаблоном переопределения, который заменяет элементы, соответствующие словарю. Однако код, который я написал, заменяет каждое совпадение. Есть ли способ игнорировать парантез и первое совпадение? Ниже приведен пример текста.

Ввод:

s = " SHOO (/ˈshuː/ suhuu) is derivered from Shi Hoo oop our something. SHOO represents title. fu oop our ( FOO ) prefers the name TOP-SHOO.[3] SHOO is one of FOO.Tu REST (tREST) means empty. tREST differs with REST. Doot Ooop Our sour (DOOs) is also means bla. DOOs are friendly."

Ожидаемый результат:

" SHOO (/ˈshuː/ suhuu) is derivered from Shi Hoo oop our is something. Shi Hoo oop our represents title. fu oop our ( FOO ) prefers the name TOP-SHOO.[3] Shi Hoo oop our is one of fu oop our.Tu REST (tREST) means empty. tu REST differs with REST. Doot Ooop Our sour (DOOs) is also means bla. Doot Ooop Our sour are friendly."
import re

d = {
'tREST':'tu REST',
'FOO': 'fu oop our',
'SHOO': 'Shi Hoo oop our',
'DOOs': 'Doot Ooop Our sour',
'TOP-SHOO' : None
}

for k, v in d.items():
    if v is None:
        d[k] = k

pattern = re.compile(r'\b(' + '|'.join(d.keys()) + r')\b')

result = pattern.sub(lambda x: d[x.group()], ' '.join(s.split()))

1 Ответ

2 голосов
/ 03 мая 2020

Хорошо, вот один из подходов. Идея состоит в том, чтобы использовать более новый модуль регулярных выражений с возможностью пропустить что-либо в скобках (это делается через (*SKIP)(*FAIL)) и реализовать кортеж с числом, а не только значениями. Наконец, мы используем функцию замены, которая подсчитывает замены:

import regex as re

# make a tuple out of it
d = {
    'tREST':    ('tu REST', 0),
    'FOO':      ('fu oop our', 0),
    'SHOO':     ('Shi Hoo oop our', 0),
    'DOOs':     ('Doot Ooop Our sour', 0),
    'TOP-SHOO': (None, 0)
}

# clear out Nones
for k, v in d.items():
    if v[0] is None:
        d[k] = (k, 0)

# pattern with r- and f-strings
pattern = re.compile(rf'''
    \([^()]+\)(*SKIP)(*FAIL)
    |
    \b{"|".join(d.keys())}\b
''', re.VERBOSE)

# here comes the magic
def replacer(match):
    key = match.group(0)
    try:
        value, cnt = d[key]
        result = value if cnt else key
        cnt += 1
        d[key] = (value, cnt)
    except KeyError:
        pass
    return result

output = pattern.sub(replacer, s)
print(output)

Немного неясно, как вы хотите обработать, например, bla bla bla (FOO). bla FOO bla bla. - замените второй FOO на второй или оставьте его так как в любом случае мы игнорируем что-либо между скобками?


Возможные оптимизации

Вы можете оставить исходный диктат таким, какой он есть, поскольку мы все равно его зацикливаем и затем может сделать кортеж из значений. Это может быть легче поддерживать (добавляя новые заменители, то есть):

# a dict
d = {
    'tREST':    'tu REST',
    'FOO':      'fu oop our',
    'SHOO':     'Shi Hoo oop our',
    'DOOs':     'Doot Ooop Our sour',
    'TOP-SHOO':  None
}

# clear out Nones
for k, v in d.items():
    if v is None:
        d[k] = (k, 0)
    else:
        d[k] = (v, 0)
...