Регулярное выражение ровно n ИЛИ m раз

105

Рассмотрим следующее регулярное выражение, где X- любое регулярное выражение.

X{n}|X{m}

Это регулярное выражение будет проверить Xпроисходящие точно n или mраз.

Есть ли квантификатор регулярного выражения, который может проверять наличие Xточно nили mраз?

Ф. Томпсон
источник
Нет . Два вхождений Xэто лучшее , что вы можете получить для общего m, n.
Джон Дворжак,
Если бы это была моя проблема, я бы попробовал использовать обратные ссылки регулярных выражений и начал бы с (X)\1{n-1}(?:\1{m-n-1}). Я знаю, что это соответствует Xхотя бы один раз, но просто для начала попробуйте эту простую вещь, а затем уточните, используя предварительные просмотры или просмотры назад вместо (X).
nalply

Ответы:

91

Не существует единого квантификатора, который означал бы «ровно m или n раз». То, как вы это делаете, в порядке.

Альтернатива:

X{m}(X{k})?

где m < nи k- значение n-m.

Марк Байерс
источник
67

Вот полный список квантификаторов (см. Http://www.regular-expressions.info/reference.html ):

  • ?, ??- 0 или 1 раз ( ??лениво, ?жадно)
  • *, *?- любое количество вхождений
  • +, +?- минимум один раз
  • {n}- точно nвхождения
  • {n,m}- nдля mвхождений, включительно
  • {n,m}?- nна mслучаи, ленивый
  • {n,}, {n,}?- хотя бы nвстреча

Чтобы получить «ровно N или M», вам нужно написать количественное регулярное выражение дважды, если только m, n не являются специальными:

  • X{n,m} если m = n+1
  • (?:X{n}){1,2} если m = 2n
  • ...
Джон Дворжак
источник
1
Зачем ?:нужно в m = 2nпримере if ? Кажется, у меня все нормально работает.
erb
7
@erb, если вы не укажете ?:, группа станет группой захвата. Помимо того, что механизм регулярных выражений запоминает вещи, которых он не должен, если у вас есть группы захвата после этой, их идентификаторы изменятся. Если вы используете свое регулярное выражение для замены, вам придется настроить замену.
John Dvorak
3

TL; DR; (?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

Похоже, вы хотите «xn раз» или «xm раз», я думаю, что буквальный перевод регулярного выражения будет (x{n}|x{m}). таким https://regex101.com/r/vH7yL5/1

или, в случае, когда у вас может быть последовательность из более чем m "x" s (при условии, что m> n), вы можете добавить 'после отсутствия "x" и', за которым не следует "x", что переводится [^x](x{n}|x{m})[^x]как Предположим, что всегда есть символ позади и после вас "x". Как видно здесь: https://regex101.com/r/bB2vH2/1

вы можете изменить его на (?:[^x]|^)(x{n}|x{m})(?:[^x]|$), что переводится как «после символа 'x' или после начала строки» и «за которым не следует 'x' или за которым следует конец строки». Но все же он не будет соответствовать двум последовательностям, между которыми будет только один символ (потому что для первого совпадения потребуется символ после, а для второго - символ перед), как вы можете видеть здесь: https://regex101.com/r/ oC5oJ4 / 1

Наконец, чтобы найти совпадение на расстоянии одного символа, вы можете добавить положительный взгляд вперед (? =) На «нет 'x' после» или положительный взгляд назад (? <=) На «нет 'x' перед», вот так: https://regex101.com/r/mC4uX3/1

(?<=[^x]|^)(x{n}|x{m})(?:[^x]|$)

Таким образом, вы сопоставите только то точное количество символов, которое вам нужно.

Усиленный
источник
1

Взглянув на ответ Enhardened, они заявляют, что их предпоследнее выражение не будет соответствовать последовательностям, между которыми находится только один символ. Есть простой способ исправить это без использования просмотра вперед / назад, а именно заменить начальный / конечный символ граничным символом. Это позволяет вам сопоставлять границы слова, включая начало / конец. Таким образом, соответствующее выражение должно быть:

(?:[^x]|\b)(x{n}|x{m})(?:[^x]|\b)

Как вы можете видеть здесь: https://regex101.com/r/oC5oJ4/2 .

rozza2058
источник
1
Круто, я не был знаком с тем, как регулярное выражение обрабатывает границы. Единственная проблема с этим методом - когда вы используете нестандартную границу. Расскажите о взгляде: regex101.com/r/j0nkeo/1 и regex101.com/r/4Ix7Dr/1
Enhardened
1
@Enhardened - это хороший момент, похоже, проблема с несколькими совпадающими группами, которые перекрываются. Это ситуация, когда вам нужно использовать взгляд назад.
rozza2058
1

Очень старый пост, но я хотел бы внести свой вклад в то, что могло бы помочь. Я пробовал точно так, как указано в вопросе, и он работает, но есть загвоздка: порядок количеств имеет значение. Учти это:

#[a-f0-9]{6}|#[a-f0-9]{3}

Это найдет все вхождения шестнадцатеричных цветовых кодов (они состоят из 3 или 6 цифр). Но когда я переворачиваю его вот так

#[a-f0-9]{3}|#[a-f0-9]{6}

он найдет только 3 цифры или первые 3 цифры из 6 цифр. Это имеет смысл, и профессионал Regex может сразу это заметить, но для многих это может быть странным поведением. Есть некоторые расширенные функции Regex, которые могут избежать этой ловушки независимо от порядка, но не все по колено в шаблонах Regex.

ДанДан
источник