Проверьте, является ли строка префиксом Javascript RegExp

В Javascript я определил регулярное выражение, и теперь пользователь вводит строку. Я хочу сказать ему, может ли его строка по-прежнему соответствовать RegExp, если он продолжает печатать или если он уже ошибается. Например:

var re = /a*b/;

"a".isPrefixOf( re ); // true
"x".isPrefixOf( re ); // false

Как может выглядеть реализация isPrefixOf?

Обновление: Спасибо за ваши ответы, что делает префикс-доказательство регулярного выражения, как было предложено брэдом, кажется хорошим решением. Но я все еще пытаюсь найти общее решение.

Возможно так: мы создаем новое регулярное выражение с пользовательским вводом, за которым следует .*. Это регулярное выражение описывает все слова, которые пользователь все еще может ввести. Если пересечение этого созданного регулярного выражения и исходного регулярного выражения является пустым, пользователь уже ошибается. Если это не так, он поправляется. Например:

var re = /a*b/;
var sInput = "a";
var reInput = new RegExp( sInput + ".*" );

reIntersection = re.intersect( reInput );
reIntersection.isEmpty(); // false

intersect() возвращает новое регулярное выражение, которое принимает только слово, которое примет как re, так и reInput. Функция еще не существует, но мы можем ее реализовать, используя опцию "вперед":

RegExp.prototype.intersect = function( pattern2 ) { 
    return new RegExp( '(?=' + this.source  + ')' + pattern2.source );
}

Остается открытым функция isEmpty(). Как мы можем проверить, если регулярное выражение Javascript соответствует любому слову или пустое?

Ответ 1

Люди, похоже, равномерно расходятся, как они интерпретируют этот вопрос, поэтому я продемонстрирую концепцию с примером Java.

import java.util.regex.*;

public class Test
{

  public static void main(String[] args) throws Exception
  {
    tryMatch("^a*b+$", "a", "ab", "abc");
  }

  public static void tryMatch(String regex, String... targets)
  {
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher("");
    System.out.printf("%nregex: %s%n", regex);
    System.out.printf("target | matches() | hitEnd()%n");
    for (String str : targets)
    {
      m.reset(str);
      System.out.printf("%-6s | %-9B | %-9B%n",
          str, m.matches(), m.hitEnd());
    }
  }
}

выход:

regex: ^a*b+$
target | matches() | hitEnd()
a      | FALSE     | TRUE
ab     | TRUE      | TRUE
abc    | FALSE     | FALSE

Целевая строка "a" не соответствует, потому что для регулярного выражения требуется хотя бы один b, но это может быть префикс успешного совпадения, поэтому hitEnd() возвращает true. Строка "ab" имеет все, что требуется для соответствия, но оно также будет соответствовать, если мы добавим больше b до конца, поэтому hitEnd() все еще возвращает true. С "abc" попытка совпадения завершится неудачей до того, как она достигнет конца целевой строки, поэтому регулярное выражение не может соответствовать любой строке, начинающейся с "abc".

Насколько я знаю, Javascript не имеет ничего похожего на метод Java hitEnd(), но возможно его подделка. Если кто-то знает, как это будет, Flagrant Badass, Стивен Левитан.

Ответ 2

Я думаю, что лучше всего сделать это для префикса Regex. Для примера, который вы дали, /a*b/, я думаю, вы могли бы использовать /a*b?/.test(userinput). Для более сложных моделей это может усложниться, но я все же думаю, что это можно сделать, вложив каждое подвыражение в ряд необязательных кванторов (?). Например:

/a*bcd*e/

Повторное выражение префикса может быть:

/a*(b(c(d*e?)?)?)?/

Это немного грязно, но я решит вашу проблему довольно хорошо.

Ответ 3

Очень интересный вопрос. В моем быстром поиске я не нашел ничего предопределенного (даже не в Perl), которое решает эту проблему.

EDIT: Ой, похоже, что у Java есть нечто подобное, называемое hitEnd() - см. ответ Алана М. То, что hitEnd() делает, говорит, что результат match() (true или false) может быть изменен дополнительным вводом. Книга "Освоение регулярных выражений" говорит, что она не очень надежна (не уверен, почему, страница 392 недоступна в книгах Google).

В зависимости от того, какие функции регулярных выражений вы используете, быстрый взлом, например, написание каких-либо префиксов вашего регулярного выражения:

например. для a + a * b + c ваши префиксы будут:

a+
a+a*
a+a*b+
a+a*b+c

и попытайтесь сопоставить любой из них с вашей строкой. Этот быстрый взлом затрудняется, если вы используете оператор выбора, если используете оператор диапазона {n, m} или обратные ссылки.

Говоря, я считаю, что хорошим решением является небольшое изменение алгоритма сопоставления.

Обычно используемый алгоритм сопоставления является алгоритмом обратного отслеживания (который хорошо работает на практике, даже если поведение наихудшего случая является экспоненциальным). Этот алгоритм успешно завершается всякий раз, когда он достиг конца регулярного выражения (даже если бы не вся цепочка была потреблена). Что вам нужно сделать, так это изменить условие завершения, чтобы оно также успешно завершалось, когда оно потребляло весь вход.

При этом вам, вероятно, придется реализовать алгоритм в JavaScript. Надеюсь, это станет частью таких библиотек, как JQuery.

Дополнительные ссылки и теория по алгоритму см. в этой статье:

http://swtch.com/~rsc/regexp/regexp1.html

(даже если он делает случай против алгоритма обратного отслеживания и предлагает алгоритм на основе FA (но FA не может обрабатывать обратные ссылки)).

Ответ 4

Сначала вы определяете свое регулярное выражение как: var re = new RegExp (/^ (здесь regexp) $/);

в событии onKeypress вы проверяете регулярное выражение следующим образом:

text.match(regexp) - текст, в который введен текст.

Является ли это ясным?

Ответ 5

Одним из способов сделать это может быть привязка к событию onKeyUp текстового поля и .test текста к регулярному выражению. Мое предположение, конечно, заключается в том, что вы хотите выполнить регулярное выражение. Я не уверен, что это именно то, что вам нужно, на самом деле ваш код:

"a".isPrefixOf( re ); // true

никогда не будет соответствовать, так как он также должен иметь следующий символ "b" (вы можете изменить регулярное выражение). Например, этот код будет проверять любую строку, соответствующую этому формату:

a-n(n)-b

Вот код, сохраните его как страницу и загрузите в браузере:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="it">
<body>
    <input type="text" size="20" id="txtData" onkeyup="showResult()" />
    <div id="dvResult" />
</body>
</html>
<script type="text/javascript">
//<![CDATA[

    theRegExp = /^a\-\d{1,2}\-b$/;

    function isPrefixOf( aText, aRegExp )
    {
        return aRegExp.test( aText );
    }

    function showResult()
    {
        res = document.getElementById( "dvResult" );
        res.innerHTML = isPrefixOf( document.getElementById( "txtData" ).value, theRegExp ) ? "Correct" : "Bad input";
    }

//]]>
</script>