Найдите, сколько слов в предложении соответствует определенным условиям

#python #regex

Вопрос:

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

Предложение состоит из группы слов. Каждое слово состоит из букв [a-zA-Z] , которые могут содержать один или несколько тире и могут заканчиваться знаком препинания, таким как точка (.), запятая (,), знак вопроса (?) или восклицательный знак (!). Каждое слово не может начинаться ни с одной буквы или цифры. Также принимается одна буква, разделенная пробелами.

Тире соединяют два или более слов в одно и должны быть приняты (но двойные тире «—» и больше нет), в то время как другие допустимые знаки препинания в конце слова должны быть удалены.

Допустимые слова (примеры):

  • «фу-фу?!.»: результат = «фу-фу»,
  • «фу-фу-фу?!.»: результат = «фу-фу-фу»,

Недопустимые слова (примеры):

  • «!@foo-foo{{}}}((«
  • «фу-фу»
  • «фу-фу@ — @фу»
  • «ф123-фу-фу-фу -«
  • «- фу-фу-фу…»

Я попытался решить проблему на python только с помощью регулярных выражений:

 import re

TESTSTR1 = 'there should be 9 valid  words, including: a well-behave, right?'
TESTSTR2 = 'blabla! bla121 {{blabla123bla.. bla-blablabla!! b;a-bla@!. blabla bla-bla-bla-bla **bla-bla'
TESTSTR3 = '{{)foo! ~~foo121 foo--foo?. foo-foo?!{. @foo-foo! f 23 foo2 f-ff-fff-ffff!.,?  **foo-f'

TESTSTR1_EXPECTED = ['there', 'should', 'be', 'valid', 'words', 'including', 'a', 'well-behave','right']
TESTSTR2_EXPECTED = ['blabla', 'bla-blablabla', 'blabla', 'bla-bla-bla-bla', 'bla-bla']
TESTSTR3_EXPECTED = ['f', 'f-ff-fff-ffff','foo-f']


def find_words(sentence: str) -> list:
    pattern_dash = r'b([^ds] (?:-w [a-zA-Z]*))b'
    pattern = r'b(?!w -w )(?!-w )[a-zA-Z] b'

    words = re.findall(pattern_dash, sentence)
    words  = re.findall(pattern, sentence)

    return words


if __name__ == "__main__":
    print('====================== TEST1 ======================')
    print(f'Expected "TESTSTR1" = {TESTSTR1_EXPECTED}')
    print(f'Result "TESTSTR1"   = {find_words(TESTSTR1)}')
        
    print('====================== TEST2 ======================')
    print(f'Expected "TESTSTR2" = {TESTSTR2_EXPECTED}')
    print(f'Result "TESTSTR2"   = {find_words(TESTSTR2)}')

    print('====================== TEST3 ======================')
    print(f'Expected "TESTSTR3" = {TESTSTR3_EXPECTED}')
    print(f'Result "TESTSTR3"   = {find_words(TESTSTR3)}')
 

Сначала я хотел найти все допустимые слова, содержащие символ тире («pattern_dash»), а затем все остальные допустимые слова (за исключением уже найденных).

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

Кто-нибудь знает, можно ли решить эту проблему, используя только регулярное выражение? У вас есть какие-нибудь идеи, как это сделать?

Большое спасибо

Ответ №1:

Чтобы получить совпадения в данных примера, вы можете использовать группу захвата.

Сначала сопоставьте пробел или * , затем запишите слова только с A-Za-z, необязательно разделенными - , и подтвердите, что слова заканчиваются пробелом, концом строки или 1 или более знаками препинания, за которыми следует граница пробела справа.

 (?:[ *]|^)([a-zA-Z] (?:-[a-zA-Z] )*)(?= |$|[.,!?:] (?!S))
 

По частям шаблон совпадает:

  • (?:[ *]|^) Не группа захвата, соответствует или или * или утверждает начало строки
  • ( Группа захвата 1
    • [a-zA-Z] Совпадение 1 вхождений A-Za-z
    • (?:-[a-zA-Z] )* При необходимости повторите то же самое еще раз перед -
  • ) Близкая группа 1
  • (?= Позитивный взгляд, утверждайте, что прямо справа находится
    • Сопоставьте пробел
    • | Или
    • $ Утвердите конец строки
    • | Или
    • [.,!?:] (?!S) Сопоставьте 1 или более вхождений из класса символов [.,!?:] и установите границу пробела справа
  • ) Близкий взгляд

Смотрите демонстрацию регулярных выражений и демонстрацию Python

Например

 import re

strings = [
     "there should be 9 valid  words, including: a well-behave, right?",
     "blabla! bla121 {{blabla123bla.. bla-blablabla!! b;a-bla@!. blabla bla-bla-bla-bla **bla-bla",
     "{{)foo! ~~foo121 foo--foo?. foo-foo?!{. @foo-foo! f 23 foo2 f-ff-fff-ffff!.,?  **foo-f"
]

pattern = r"(?:[ *]|^)([a-zA-Z] (?:-[a-zA-Z] )*)(?= |$|[.,!?:] (?!S))"
for s in strings:
     print(re.findall(pattern, s, re.M))
 

Выход

 ['there', 'should', 'be', 'valid', 'words', 'including', 'a', 'well-behave', 'right']
['blabla', 'bla-blablabla', 'blabla', 'bla-bla-bla-bla', 'bla-bla']
['f', 'f-ff-fff-ffff', 'foo-f']