Узнайте позицию, где регулярное выражение не удалось

javascript regex lexical-analysis

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. Группа 1 тестирует \w+токен
  2. Группа 2, кажется, проверяет \w+\d+, поэтому, постепенно, она проверяет \d+токен
  3. Группа 3, кажется, проверяет \w+\d+$, поэтому, постепенно, она проверяет $токен

Есть три группы захвата. Если все три заданы, матч будет полным успехом. Если только Группа 3 не установлена ​​(как с abc123a), вы можете сказать, что $причиной сбоя. Если группа 1 установлена, но не группа 2 (как с abc), вы можете сказать, что \d+причиной сбоя.

Для справки: Внутренний вид пути отказа

Для чего это стоит, вот представление пути отказа от отладчика RegexBuddy.

RegexBuddy Debug

Автор: zx81 Размещён: 23.05.2014 11:41

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

Автор: guest271314 Размещён: 18.06.2016 05:25

0 плюса

Есть ли способ узнать позицию в строке, которая вызвала сбой регулярного выражения?

Нет, нет Regex либо соответствует, либо нет. Ничего промежуточного.

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

Возьми Струну Hello my Worldи Узор /Hello World/. В то время как каждое слово будет соответствовать индивидуально, все выражение не будет выполнено. Вы не можете сказать , является ли Helloили Worldсогласованы - независимые, оба делают. Также пробелы между ними доступны.

Автор: dognose Размещён: 23.05.2014 11:27
Вопросы из категории :
32x32