регулярное выражение с дополнительной группой захвата

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

So:

  • «2 кг воды» должны вернуть: 2, kg, water
  • «1 галлон сырой нефти» должен вернуть: 1, gallon, oil

Я могу добиться этого с помощью следующего регулярного выражения: (\d*) ?(kg|ml|gallon).*(water|oil)

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

  • «1 галлон дизельного топлива» должен вернуть: 1, gallon или 1, gallon, ''

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

Вот текущие параметры в онлайн-тестере регулярных выражений: https://regex101.com/r/hV3wQ3/55


person JasperZelf    schedule 07.03.2018    source источник
comment
Почему бы вам просто не захватить (\d+) ?(kg|ml|gallon) of (.*) и позже проверить список заранее определенных веществ?   -  person melpomene    schedule 07.03.2018
comment
Используйте квантификатор ?, который соответствует от нуля до единицы. (water|oil)?   -  person Srdjan M.    schedule 07.03.2018
comment
@ S.Jovan Это не работает. Он всегда ничего не улавливает.   -  person melpomene    schedule 07.03.2018


Ответы (1)


Вы пытаетесь использовать (\d+) ?(kg|ml|gallon).*(?:(water|oil))?, и этот шаблон не может захватить water / oil. Проблема в том, что .* захватывает любые символы 0+, кроме символов разрыва строки, до конца строки / строки, и (?:(water|oil))? пробуется, когда индекс регулярного выражения присутствует в конце строки. Поскольку (?:(water|oil))? может соответствовать пустой строке, он соответствует положению в конце строки, и совпадение возвращается.

Вы по-прежнему можете использовать группу захвата как обязательную, но оберните .* и группу захвата необязательной группой без захвата:

(\d+) ?(kg|ml|gallon)(?:.*(water|oil))?
                     ^^^             ^^ 

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

(?:.*(water|oil))? соответствует 1 или 0 (жадно) вхождению любых символов 0+, кроме символов разрыва строки (.*), а затем либо water, либо oil.

person Wiktor Stribiżew    schedule 07.03.2018
comment
Хорошо, это работает как шарм. Я вижу, как это работает. Но я не понимаю, почему (\d+) ?(kg|ml|gallon).*(?:(water|oil))? не работает. Почему это не соответствует (вода | масло) в группе 3? - person JasperZelf; 07.03.2018
comment
@JasperZelf Потому что .* уже захватил water или oil, и нет необходимости возвращаться, поскольку (...)? соответствует чему угодно, даже пустой строке (местоположению). - person Wiktor Stribiżew; 07.03.2018
comment
Это имеет прекрасный смысл. Спасибо за объяснение - person JasperZelf; 08.03.2018