Регулярное выражение: индивидуально необязательные группы захвата, но в совокупности должна существовать хотя бы одна

#regex

#регулярное выражение

Вопрос:

Я реализовал это с несколькими регулярными выражениями, но мне интересно знать, возможно ли это сделать с одним.

У меня есть несколько строк, представляющих длительность. Эти строки имеют формат, иллюстрируемый «4d10h30m», представляющий продолжительность в четыре дня, десять часов и тридцать минут. Каждая единица измерения продолжительности (дни, часы или минуты) необязательна, поэтому «4d» является допустимой строкой, как и «10h30m».

Что я хотел бы, так это регулярное выражение (javascript, если это имеет значение), которое надежно возвращает три группы захвата, каждая из которых содержит значение единицы. Итак, в примере «4d10h30m» сопоставление регулярного выражения с этой строкой должно возвращать [«4», «10», «30»]. Если эта единица отсутствует, ее место в кортеже может содержать практически все, что не является ненулевым целым числом (0, «0», null или пустая строка — все в порядке).

Два подхода, которые я рассмотрел, заключаются в следующем:

 /(?:(d )d)?(?:(d )h)?(?:(d )m)?/
  

которая соответствует пустой строке; и некоторые варианты:

 /((?:d [dhm]){1,3})/
  

что затрудняет захват только d и возвращает неопределенное количество групп захвата.

Я подозреваю, что последнее не является стартовым. Первое работало бы, если бы существовала конструкция регулярного выражения, которая указывает «любая из этих групп по отдельности необязательна, но в совокупности должна присутствовать хотя бы одна из них»? Это кажется выполнимым при ограничениях клеточных автоматов, но я не знаю, как это будет реализовано в регулярном выражении, или даже если это возможно.

Редактировать:

По запросу, некоторые примеры входных данных и их выходных данных:

 2d1h5m # ["2","1","5"]
3h20m  # ["", "3", "20"]
4d10m  # ["4", "", "10"]
2d     # ["2", "", ""]
6h     # ["", "6", ""]
1x20y  # no match (invalid units)
2dh20m # no match (no units allowed without a value)
21020  # no match (no units)
1h2d5m # no match (disordered units)
xd5m   # no match (non-numeral value)
  

Комментарии:

1. Не могли бы вы использовать первую и просто проверить наличие пустой строки впоследствии?

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

3. Может ли модуль полностью отсутствовать? Например, 4d5m? Или они все есть, но без цифр, например, 4d0h5m или 4dxh5m? Еще лучше, можете ли вы предоставить образец допустимого ввода и показать, что должно быть записано в 3 группах для каждой?

4. Я добавил несколько примеров входных и выходных данных в основную часть вопроса, которые должны ответить на эти вопросы.

Ответ №1:

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

 ^(?!$)(?:(d )d)?(?:(d )h)?(?:(d )m)?$
  

Выражение (?!$) означает «за этой точкой не должен следовать конец ввода», а при привязке к началу ввода ^(?!$) означает «за началом не может следовать конец», что то же самое, что сказать «должен быть какой-то ввод».

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

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

Ответ №2:

Приведенное ниже регулярное выражение будет фиксировать числа, dhm присутствуют они или нет во входной строке,

 (d)d?(d{2})h?(d{2})m?
  

ДЕМОНСТРАЦИЯ