В вашем последнем регулярном выражении [1]
re.search(r'\bU\W+?S\b\W+?N\b\W+?S\b', text)
вы не получите совпадения, потому что вы сделали несколько ошибок:
\w+
означает один или несколько символов слова, \W+
для одного или нескольких символов, отличных от слова. - привязка границы
\b
иногда находится не в том месте (например, между начальной буквой и остальной частью слова)
re.search(r'\bU\w+\sS\w+?\sN\w+?\sS\w+', text)
должно совпадать.
И, ну,
print(re.search(r'\bu\w+?g\w+\sf\w+', text))
, конечно, соответствует underground facility
, но в длинном тексте нерелевантных совпадений будет гораздо больше.
Подход к обобщению
Наконец, я построил небольшую «машину», которая динамически создает регулярные выражения из известных сокращений:
import re
text = '''They posted out the United States Navy Seals (USNS) to the area.
Entrance was through an underground facility (UGF) as they has to bypass a no-fly-zone (NFZ).
I found an assault-rifle (AR) in the armoury.'''
abbrs = ['USNS', 'UGF', 'NFZ', 'AR']
for abbr in abbrs:
pattern = ''.join(map(lambda i: '['+i.upper()+i.lower()+'][a-z]+[ a-z-]', abbr))
print(pattern)
print(re.search(pattern, text, flags=re.IGNORECASE))
Результатом приведенного выше скрипта является:
[Uu][a-z]+[ a-z-][Ss][a-z]+[ a-z-][Nn][a-z]+[ a-z-][Ss][a-z]+[ a-z-]
<re.Match object; span=(20, 45), match='United States Navy Seals '>
[Uu][a-z]+[ a-z-][Gg][a-z]+[ a-z-][Ff][a-z]+[ a-z-]
<re.Match object; span=(89, 110), match='underground facility '>
[Nn][a-z]+[ a-z-][Ff][a-z]+[ a-z-][Zz][a-z]+[ a-z-]
<re.Match object; span=(140, 152), match='no-fly-zone '>
[Aa][a-z]+[ a-z-][Rr][a-z]+[ a-z-]
<re.Match object; span=(170, 184), match='assault-rifle '>
Дальнейшее обобщение
Если мы предположим, что в тексте каждая аббревиатура вводится после первого появления соответствующей длинной формы, и мы далее предполагаем, что способ ее написания определенно начинается с границы слова и определенно заканчивается границей слова (никаких предположений о заглавных буквах и использование дефисов), мы можем попытаться автоматически извлечь глоссарий следующим образом:
import re
text = '''They posted out the United States Navy Seals (USNS) to the area.
Entrance was through an underground facility (UGF) as they has to bypass a no-fly-zone (NFZ).
I found an assault-rifle (AR) in the armoury.'''
# build a regex for an initial
def init_re(i):
return f'[{i.upper()+i.lower()}][a-z]+[ -]??'
# build a regex for an abbreviation
def abbr_re(abbr):
return r'\b'+''.join([init_re(i) for i in abbr])+r'\b'
# build an inverse glossary from a text
def inverse_glossary(text):
abbreviations = set(re.findall('\([A-Z]+\)', text))
igloss = dict()
for pabbr in abbreviations:
abbr = pabbr[1:-1]
pattern = '('+abbr_re(abbr)+') '+r'\('+abbr+r'\)'
m = re.search(pattern, text)
if m:
longform = m.group(1)
igloss[longform] = abbr
return igloss
igloss = inverse_glossary(text)
for long in igloss:
print('{} -> {}'.format(long, igloss[long]))
Результат будет
no-fly-zone -> NFZ
United States Navy Seals -> USNS
assault-rifle -> AR
underground facility -> UGF
Используя inv В таком глоссарии вы можете легко заменить все длинные формы соответствующими сокращениями. Немного сложнее сделать это для всех, кроме первого случая. Есть много места для уточнения, например, для правильной обработки разрывов строк в длинных формах (также можно использовать re.compile ).
Что касается замены аббревиатур на длинные формы, вы должны построить обычный глоссарий вместо обратного:
# build a glossary from a text
def glossary(text):
abbreviations = set(re.findall('\([A-Z]+\)', text))
gloss = dict()
for pabbr in abbreviations:
abbr = pabbr[1:-1]
pattern = '('+abbr_re(abbr)+') '+r'\('+abbr+r'\)'
m = re.search(pattern, text)
if m:
longform = m.group(1)
gloss[abbr] = longform
return gloss
gloss = glossary(text)
for abbr in gloss:
print('{}: {}'.format(abbr, gloss[abbr]))
Результат здесь будет
AR: assault-rifle
NFZ: no-fly-zone
UGF: underground facility
USNS: United States Navy Seals
Сама замена предоставляется читателю.
[1] Давайте еще раз подробнее рассмотрим ваше первое регулярное выражение :
re.search(r'\bUnited\W+?States\b\W+?Navy\b\W+?Seals\b', text)
Граничные привязки (\b
) избыточны. Их можно удалить, ничего не меняя в результате, потому что \W+?
означает хотя бы один символ, не являющийся словом, после последнего символа States
и Navy
. Здесь они не вызывают проблем, но я предполагаю, что они привели к путанице, когда вы начали, изменив его, чтобы получить более общий.