Итак, вы хотите знать, как это работает ...
Регулярные выражения не имеют реального понимания значений чисел в вашей строке, их интересует только то, как они представлены, поэтому поиск чисел в диапазоне кажется более неловким, чем должно быть. Единственная причина, по которой ваш движок регулярных выражений может понимать диапазон в классе символов , например [0-9]
, заключается в том, что позиции символов в списке (диапазон символов, например [&-~]
) столь же действителен и одинаково понятен для него.)
Итак, чтобы соответствовать диапазону, например, 99-9999999, вам нужно разобрать, как это выглядит: буквальное «99», или три цифры без начального нуля, или четыре цифры без начального нуля и т. Д.
Но это то, что сделал демо , верно? И это не сработало. Из вашей тестовой строки "9293" ваше регулярное выражение соответствует только 929. Здесь произошло то, что движок регулярных выражений жаждет вернуть полное совпадение - как только он найдет одно, он вернет его, даже если позднее было возможно лучшее / более длинное совпадение.
Вот как произошло это совпадение. (Я пропущу некоторые детали, такие как группировка , так как они здесь не очень важны.)
Шаг 1.
Движок сравнивает первый токен в регулярном выражении с первым символом в строке
(<strong><em><kbd>9</kbd></em></strong>9|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9])
<kbd><strong><em>9</em></strong></kbd>293
✅
Успех, они совпадают.
Шаг 2.
Затем движок продвигается к следующему токену в регулярном выражении и следующему символу в строке и сравнивает их.
(<strong><em>9</em></strong><kbd>9</kbd>|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9])
<strong><em>9</em></strong><kbd>2</kbd>93
❌
Ошибка, нет совпадения. Двигатель остановится и вернет ошибку здесь, но вы используете чередование через |
, поэтому он знает, что есть альтернативное выражение для попытки.
Шаг 3.
Движок переходит к первому токену следующего альтернативного выражения в регулярном выражении и перематывает позицию в строке.
(99|<strong><em><kbd>[1-9]</kbd></em></strong>[0-9][0-9]|[1-9][0-9][0-9][0-9])
<strong><em><kbd>9</kbd></em></strong>293
✅
Успех, они совпадают.
Шаг 4.
Продолжение.
(99|<strong><em>[1-9]<kbd>[0-9]</kbd></em></strong>[0-9]|[1-9][0-9][0-9][0-9])
<strong><em>9<kbd>2</kbd></em></strong>93
✅
Match.
Шаг 5.
И снова.
(99|<strong><em>[1-9][0-9]<kbd>[0-9]</kbd></em></strong>|[1-9][0-9][0-9][0-9])
<strong><em>92<kbd>9</kbd></em></strong>3
✅
Успех. Полное выражение соответствует. Нет необходимости пробовать оставшуюся альтернативу. Возвращенное совпадение:
<strong><em>929</em></strong>
Как вы, наверное, поняли, если бы ваша входная строка была вместо "9923", то шаг 2 соответствовал бы и двигатель там остановился бы и возвратил "99" .
Как вы, вероятно, уже поняли, если вы переставите свои альтернативные выражения с самого длинного на самое короткое
([1-9][0-9][0-9][0-9]|[1-9][0-9][0-9]|99)
сначала будет предпринята самая длинная попытка, которая будет соответствовать и вернуть ожидаемое "9293" .
Упрощен
Это все еще довольно многословно, тем более, что вы увеличиваете количество цифр в вашем диапазоне. Есть несколько вещей, которые вы можете сделать, чтобы упростить его.
Класс символов [0-9]
может быть представлен классом символов \d
.
([1-9]\d\d\d|[1-9]\d\d|99)
И вместо того, чтобы повторять их, используйте квантификатор в фигурных скобках, например:
([1-9]\d{3}|[1-9]\d{2}|99)
Как это бывает, квантификаторы также могут принимать форму {min, max}
, поэтому вы можете объединить два аналогичных альтернативных варианта:
([1-9]\d{2,3}|99)
Вы могли бы ожидать, что это вернет вас назад, возвращая «929» снова, двигатель жадный и все такое, но квантификаторы по умолчанию жадные , поэтому они попытаются собрать как можно больше. Это хорошо подходит для большего желаемого диапазона:
([1-9]\d{2,6}|99)
Завершение
То, что вы будете делать с этим, зависит от того, что вам нужно сделать для регулярного выражения. Так как скобки излишни, нет смысла создавать группу захвата всего регулярного выражения. Однако решение приходит, когда у вас есть строка ввода, например:
Скорее всего, вас съедят на 1000 гр.
Если уВы пытаетесь выяснить, сколько грёсов собирается съесть вас, вы можете использовать
[1-9]\d{2,6}|99
, который будет возвращать 1000 .
Однако эта Сорта возвращается к исходной проблеме с вашей демонстрацией. Если это значение «12345678 grue», которое находится за пределами диапазона, это будет соответствовать «1234567», что может быть не тем, что вы хотите. Вы можете убедиться, что за выбранным вами номером не сразу следует (или не предшествует) другая цифра, используя отрицательные выражения .
(?<!\d)([1-9]\d{2,6}|99)(?!\d)
(?<!\d)
означает «с этой позиции, предыдущий символ не является цифрой», а (?!\d)
означает «с этой позиции, следующий символ не является цифрой».
Круглые скобки вокруг альтернатив возвращаются, так как они необходимы здесь для группировки, в противном случае lookbehind будет только частью и применяется в первом альтернативном выражении, а lookahead будет только частью и применяется во втором альтернативе.
С другой стороны, если вы пытаетесь убедиться, что вся строка only состоит из числа в вашем диапазоне, вы хотите вместо этого использовать якоря ^
и $
(начало строки и конец строки соответственно):
^([1-9]\d{2,6}|99)$
И, наконец, вы можете обменять группу захвата на группу без захвата (?:...)
, поэтому:
^(?:[1-9]\d{2,6}|99)$
или
(?<!\d)(?:[1-9]\d{2,6}|99)(?!\d)
Вы по-прежнему будете захватывать число во время матча, оно просто не будет повторяться при групповом захвате. (Lookarounds уже не захватывает, не нужно беспокоиться о них.)