Узнайте позицию, где регулярное выражение не удалось
3418 просмотра
3 ответа
Я пытаюсь написать лексер в JavaScript для поиска токенов простого предметно-ориентированного языка. Я начал с простой реализации, которая просто пытается сопоставить последующие регулярные выражения с текущей позицией в строке, чтобы выяснить, соответствует ли она некоторому формату токена, и принять его тогда.
Проблема в том, что когда что-то не совпадает внутри такого регулярного выражения, все регулярное выражение терпит неудачу, поэтому я не знаю, какой именно символ вызвал его неудачу.
Есть ли способ узнать позицию в строке, которая вызвала сбой регулярного выражения?
INB4: я не спрашиваю об отладке моего регулярного выражения и проверке его правильности. Это уже правильно, совпадает с правильными строками и сбрасывает неправильные. Я просто хочу программно узнать, где именно регулярное выражение перестало совпадать, чтобы узнать положение символа, который был неправильным при вводе пользователем, и сколько из них было в порядке.
Есть ли способ сделать это с помощью простых регулярных выражений, вместо того, чтобы реализовывать полноценный конечный автомат?
Автор: SasQ Источник Размещён: 12.11.2019 09:13Ответы (3)
27 плюса
Краткий ответ
Не существует такого понятия, как «позиция в строке, которая приводит к сбою регулярного выражения».
Тем не менее, я покажу вам подход, чтобы ответить на обратный вопрос:
По какому токену в регулярном выражении двигатель стал не в состоянии соответствовать строке?
обсуждение
На мой взгляд, вопрос в the position in the string which caused the regular expression to fail
обратном порядке. Когда движок движется вниз по строке левой рукой, а шаблон - правой рукой, токен регулярного выражения, который соответствует шести символам в одно мгновение, может позже, из-за квантификаторов и обратного отслеживания, быть уменьшен до совпадения с нулевыми символами в следующий - или расширен до соответствия десять.
На мой взгляд, более правильный вопрос будет:
По какому токену в регулярном выражении двигатель стал не в состоянии соответствовать строке?
Например, рассмотрим регулярное выражение ^\w+\d+$
и строку abc132z
.
\w+
Может фактически совпадает со строкой. Тем не менее, все регулярное выражение терпит неудачу. Имеет ли смысл говорить, что регулярное выражение терпит неудачу в конце строки? Я так не думаю. Учти это.
Первоначально, \w+
будет соответствовать abc132z
. Затем двигатель переходит к следующей лексемы: \d+
. На этом этапе двигатель возвращается в строку, постепенно позволяя \w+
отказаться от 2z
(так, чтобы \w+
теперь только соответствовало abc13
), позволяя \d+
совпадать 2
.
На этом этапе $
утверждение не выполняется, так как z
осталось. Двигатель откатывает назад, позволяя \w+
, отказаться от 3
персонажа, затем 1
(так, чтобы \w+
теперь только соответствовало abc
), в конечном итоге позволяя \d+
совпадать 132
. На каждом шаге двигатель проверяет $
утверждение и дает сбой. В зависимости от внутренних компонентов двигателя может произойти больше возврата: « \d+
снова» откажется от 2 и 3, затем \w+
откажется от «c» и «b». Когда двигатель, наконец, сдается, \w+
единственное совпадает с начальным a
. Можете ли вы сказать, что регулярное выражение "терпит неудачу на" 3 "? На" b "?
Нет. Если вы смотрите на шаблон регулярных выражений слева направо, вы можете утверждать, что он не работает $
, потому что это первый токен, который мы не смогли добавить к совпадению. Имейте в виду, что есть и другие способы утверждать это.
Ниже я дам вам скриншот, чтобы визуализировать это. Но сначала давайте посмотрим, сможем ли мы ответить на другой вопрос.
Другой вопрос
Существуют ли методы, которые позволяют нам ответить на другой вопрос:
По какому токену в регулярном выражении двигатель стал не в состоянии соответствовать строке?
Это зависит от вашего регулярного выражения. Если вы можете нарезать свое регулярное выражение на чистые компоненты, то вы можете разработать выражение с серией необязательных предварительных просмотров внутри групп захвата, что позволит всегда соответствовать совпадению. Первая неустановленная группа захвата - та, которая вызвала сбой.
Javascript немного скуп на необязательные запросы, но вы можете написать что-то вроде этого:
^(?:(?=(\w+)))?(?:(?=(\w+\d+)))?(?:(?=(\w+\d+$)))?.
В PCRE, .NET, Python ... вы могли бы написать это более компактно:
^(?=(\w+))?(?=(\w+\d+))?(?=(\w+\d+$))?.
Что здесь происходит? Каждый прогноз основывается на последнем, добавляя по одному токену за раз. Поэтому мы можем проверить каждый токен отдельно. Точка в конце - необязательный эффект для визуальной обратной связи: в отладчике мы видим, что сопоставлен хотя бы один символ, но нам не важен этот символ, нам нужны только группы захвата.
- Группа 1 тестирует
\w+
токен - Группа 2, кажется, проверяет
\w+\d+
, поэтому, постепенно, она проверяет\d+
токен - Группа 3, кажется, проверяет
\w+\d+$
, поэтому, постепенно, она проверяет$
токен
Есть три группы захвата. Если все три заданы, матч будет полным успехом. Если только Группа 3 не установлена (как с abc123a
), вы можете сказать, что $
причиной сбоя. Если группа 1 установлена, но не группа 2 (как с abc
), вы можете сказать, что \d+
причиной сбоя.
Для справки: Внутренний вид пути отказа
Для чего это стоит, вот представление пути отказа от отладчика RegexBuddy.
1 плюс
Вы можете использовать отрицательный набор символов RegExp
,
[^xyz] [^a-c]
Отрицательный или дополненный набор символов. То есть он соответствует всему, что не заключено в квадратные скобки. Вы можете указать диапазон символов, используя дефис, но если дефис отображается как первый или последний символ, заключенный в квадратные скобки, он воспринимается как буквальный дефис, который включается в набор символов как обычный символ.
index
собственностью String.prototype.match()
Возвращаемый массив имеет дополнительное входное свойство, которое содержит исходную строку, которая была проанализирована. Кроме того, у него есть свойство index, которое представляет нулевой индекс совпадения в строке.
Например, чтобы войти, index
где цифра соответствует RegExp
/[^a-zA-z]/
в строкеaBcD7zYx
var re = /[^a-zA-Z]/;
var str = "aBcD7zYx";
var i = str.match(re).index;
console.log(i); // 4
0 плюса
Есть ли способ узнать позицию в строке, которая вызвала сбой регулярного выражения?
Нет, нет Regex либо соответствует, либо нет. Ничего промежуточного.
Частичные выражения могут совпадать, но не весь шаблон. Таким образом, движок всегда должен оценивать все выражение:
Возьми Струну Hello my World
и Узор /Hello World/
. В то время как каждое слово будет соответствовать индивидуально, все выражение не будет выполнено. Вы не можете сказать , является ли Hello
или World
согласованы - независимые, оба делают. Также пробелы между ними доступны.
Вопросы из категории :
- javascript Как определить, какой из указанных шрифтов был использован на веб-странице?
- javascript Валидация клиентской стороны ASP.Net
- javascript Длина объекта JavaScript
- javascript Получение текста из выпадающего списка
- javascript Скрипт входа со скрытыми кнопками
- javascript Как автоматически изменить размер текстовой области с помощью Prototype?
- regex Learning Regular Expressions
- regex Regex и unicode
- regex Мое регулярное выражение слишком подходит. Как мне это остановить?
- regex Как выполнить подстановку Perl для строки, сохранив оригинал?
- regex Замена n-го экземпляра совпадения с регулярным выражением в Javascript
- regex Как заменить простые URL ссылками?
- lexical-analysis Синтаксический смысл из текста
- lexical-analysis Практическое различие между правилами парсера и правилами лексера в ANTLR?
- lexical-analysis PHP Лексер и Парсер Генератор?
- lexical-analysis Узнайте позицию, где регулярное выражение не удалось
- lexical-analysis Написание очень простого лексического анализатора на C ++
- lexical-analysis Почему именно этот запрос Lucene не возвращает хитов?