Я пытаюсь сопоставить фрагмент предложения формы «через 3 дня» или «через 1 месяц». Я хочу быть особенно внимательным к формам единственного и множественного числа, поэтому «1 день» допустим, а «1 день» — нет.
У меня есть следующий код, который почти готов, но первые две записи в тестах на отказ не дают сбоя. Любые предложения, пожалуйста, используйте синтаксическую нотацию, поскольку я хотел бы, если возможно, избежать set_parse_action(), который проверяет числовое значение на соответствие множеству единиц.
from pyparsing import *
units = Keyword('days') ^ Keyword('months')
unit = Keyword('day') ^ Keyword('month')
single = Literal('1') + unit
multi = Word(nums) + units
after = Keyword('after') + ( single ^ multi )
a = after.run_tests('''
after 1 day
after 2 days
after 1 month
after 2 months
''')
print('=============')
b = after.run_tests('''
after 1 days
after 2 day
after 1day
after 2days
''', failure_tests = True)
print('Success tests', 'passed' if a[0] else 'failed')
print('Failure tests', 'passed' if b[0] else 'failed')
Поздравляем, вы задали тысячный вопрос о пипарсинге! stackoverflow.com/questions/tagged/pyparsing
Проходит только случай after 1 days
, когда он должен завершиться неудачей, остальные три случая завершаются неудачей, как и ожидалось.
Проблема в том, что проверка multi = Word(nums) + units
использует nums
, включающую 1, поэтому даже если ваш вариант в единственном числе не работает, этот сработает. Я посмотрел, как определяются числа, видимо, это nums = '0123456789'
(см. здесь). Следовательно, вы удаляете 1. У меня это работает:
...
multi_nums = '023456789' # nums excluding 1
single = Literal('1') + unit
multi = Word(multi_nums) + units
...
Обновлено:
Вышеупомянутое не работает для двузначных цифр, включая 1, см. комментарии. Исправленная версия согласно комментариям:
single = Literal('1') + unit
multi = ~Keyword('1') + Word(nums) + units
Вы на правильном пути, но это не будет соответствовать «через 10 дней». Чтобы отклонить одиночную цифру «1» во множественном числе, вы можете использовать такой отрицательный просмотр вперед multi = ~Keyword("1") + Word(nums) + units
(ключевое слово необходимо, чтобы соответствовать только одной цифре «1», но не первой «1» из «10») или Такое регулярное выражение multi = Regex(r"[1-9]\d+|[2-9]") + units
.
@BruceH Внес изменения.
Спасибо aRTy и спасибо Полу — я постепенно начинаю понимать, как работает ваш парсер.
Чтобы научиться правильно обрабатывать первую цифру, я предлагаю добавить «через 10 дней» и «через 21 день» к тестам на успех и «через 10 дней» и «после 21 дня» к тестам на отказ.