Есть ли способ улучшить это регулярное выражение? - PullRequest
6 голосов
/ 23 августа 2010

Я новичок в регулярных выражениях, поэтому буду признателен за отзыв коллег.Он будет активно использоваться на моем сайте, поэтому любые странные крайние случаи могут привести к хаосу.Идея состоит в том, чтобы ввести количество ингредиента в рецепте в целых единицах или фракциях.Из-за моего механизма автозаполнения, также допустимо только число (так как оно появится в выпадающем списке).Эти строки действительны:

1
1/2
1 1/2
4 cups
4 1/2 cups
10 3/4 cups sliced

Числовая часть строки должна быть отдельной группой, чтобы я мог разобрать ее с помощью моего анализатора дроби.Все после числовой части должно быть второй группой.Сначала я попробовал это:

^\s*(\d+|\d+\/\d+|\d+\s*\d+\/\d+)\s*(.*)$

Это почти работает, но "1 1/2 чашки" будет анализироваться как (1) (1/2 чашки) вместо (1 1/2) и(чашки).Немного почесав голову, я решил, что это из-за порядка в моем предложении «ИЛИ».(1) удовлетворяет \ d +, а (. *) Удовлетворяет остальному.Поэтому я изменил это на:

^\s*(\d+\/\d+|\d+\s*\d+\/\d+|\d+)\s*([a-z].*)$

Это почти работает, но допускает такие странности, как "1 1/2/4 чашки" или "1/2 3 чашки".Поэтому я решил использовать букву в качестве первого символа после допустимого числового выражения:

^\s*(\d+\/\d+|\d+\s*\d+\/\d+|\d+)\s*($|[a-z].*)$

Примечание. Я запускаю это в режиме без учета регистра.Вот мои вопросы:

  1. Можно ли улучшить выражение?Мне не нравится список «ИЛИ» для числа, дроби, составной дроби, но я не мог придумать, как разрешить целые числа, дроби или составные дроби.

  2. Было бы очень хорошо, если бы я мог вернуть группу для каждого слова после числового компонента.Например, группа для (10 3/4), группа для (чашки) и группа для (нарезанная).После может быть любое количество слов.Возможно ли это?

Спасибо!

Ответы [ 2 ]

3 голосов
/ 23 августа 2010

Что ж, мне кажется, что вам вообще не нужны условия ИЛИ (но см. Ниже).

Для числового бита, вы можете сойти с:

\d+(\s+\d+/\d+)

, который будет обрабатывать все эти дробные значения.

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

^\s*((\d+\s)?(\d+/\d+)?|\d+(\.\d+)?)\s*([a-z].*)?$
 |   |                  |           |  |
 |   |                  |           |  +--- start of alpha section.
 |   |                  |           +------ optional white space.
 |   |                  +------------------ decimal (nn[.nn])
 |   +------------------------------------- fractional ([nn ][nn/nn])
 +----------------------------------------- optional starting space.

хотя это допускает пустое дробное количество, так что вам будет лучше с тем, что у вас есть (целое, дробное и десятичное в отдельных предложениях ИЛИ).

Я предпочитаю конструкцию ([a-z].*)?$ самому ($|[a-z].*)$, но это может быть просто отвращением к моему прошлому иметь несколько маркеров конца строки в моем RE: -)


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

Вам действительно нужно ограничивать то, что вводится. Я видел рецепты, которые требуют a pinch of salt и a handful of sultanas. Я лично думаю, что вы можете быть ограничены в том, что вы позволите. У меня было бы поле свободной формы для количества и раскрывающийся список для типа продуктов питания (фактически, я бы, вероятно, просто разрешил бы использовать произвольную форму для лота, если бы я не предлагал возможность поиска рецептов на основе того, что находится в холодильнике).

1 голос
/ 23 августа 2010

Я считаю, что это регулярное выражение должно делать то, что вы хотите:

/^\s*(\d+ \d+\/\d+|\d+\/\d+|\d+)\s*(.*)/

Для сопоставления конкретных слов вы должны просто разбить пробел после разбора. Есть некоторые вещи, которые вы не хотите делать с регулярными выражениями;)

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