【问题标题】:Check if string is a prefix of a Javascript RegExp检查字符串是否是 Javascript RegExp 的前缀
【发布时间】:2009-01-06 13:01:49
【问题描述】:

在 Javascript 中,我定义了一个正则表达式,现在用户正在输入一个字符串。我想告诉他,如果他继续打字或者他已经走错了路,他的字符串是否仍然可以匹配正则表达式。例如:

var re = /a*b/;

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

isPrefixOf 的实现是什么样的?

更新:感谢您的回答,按照 brad 的建议,使正则表达式前缀证明似乎是一个很好的解决方法。但我仍在努力寻找一个通用的解决方案。

也许这样:我们创建一个新的正则表达式,用户输入后跟.*。这个正则表达式描述了用户仍然可以输入的所有单词。如果这个创建的正则表达式和原始正则表达式的交集是空的,那么用户已经走错路了。如果不是,他过得很好。例如:

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

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

intersect() 返回一个新的正则表达式,它只接受 rereInput 都接受的单词。该功能尚不存在,但我们可以使用前瞻实现它:

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

仍然打开的是isEmpty() 函数。我们如何检查 Javascript 正则表达式是否匹配任何单词或是否为空?

【问题讨论】:

    标签: javascript regex prefix


    【解决方案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() 方法那样的东西,但有可能伪造它。如果有人知道怎么做,那就是那个无耻的坏蛋,Steven Levithan

    【讨论】:

      【解决方案2】:

      我认为你最好的选择是让你的正则表达式前缀证明。对于您给出的示例/a*b/,我认为您可能可以使用/a*b?/.test(userinput)。对于更复杂的模式,这可能会变得越来越困难,但我仍然认为可以通过将每个子表达式嵌套在一系列可选量词中来完成 (?)。例如:

      /a*bcd*e/
      

      前缀正则表达式可以是:

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

      它有点乱,但我认为会很好地解决你的问题。

      【讨论】:

      • 这是一个好主意(你得到了赞成票),但我认为对于更复杂的模式来说很快就会崩溃。您将如何为以下模式创建前缀正则表达式? /a(bc|BC)d/
      【解决方案3】:

      非常有趣的问题。在我的快速搜索中,我没有找到任何可以解决这个问题的预定义(甚至在 Perl 中)。

      编辑:哎呀,似乎 Java 有类似的东西叫做 hitEnd() - 请参阅 Alan M 的回答。 hitEnd() 的作用是说 match() 的结果(真或假)可能会被额外的输入修改。 《掌握正则表达式》这本书说它不是很可靠(不知道为什么,谷歌书籍中没有第 392 页)。

      根据您使用的正则表达式的哪些功能,快速破解,例如为您的正则表达式编写某种前缀:

      例如对于 a+a*b+c 你的前缀是:

      一个+ 一个+一个* a+a*b+ a+a*b+c

      并尝试将它们中的任何一个与您的字符串匹配可能会起作用。如果您使用选择运算符、使用范围运算符 {n,m} 或反向引用,则此快速破解将变得困难。

      话虽如此,我认为好的解决方案是稍微修改匹配算法。

      通常采用的匹配算法是回溯算法(在实践中效果很好,即使最坏情况下的行为是指数的)。只要到达正则表达式的末尾,该算法就会成功终止(即使没有消耗整个字符串)。您需要做的是修改终止条件,使其在消耗完所有输入后也成功终止。

      话虽如此,您可能必须在 JavaScript 中实际实现该算法。希望这将成为 Jquery 等库的一部分。

      有关算法的更多参考资料和理论,请查看这篇文章:

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

      (即使它反对回溯算法并建议基于 FA 的算法(但 FA 无法处理反向引用)。

      【讨论】:

        【解决方案4】:

        这样做的一种方法是挂钩文本框的 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>
        

        【讨论】:

        • 你应该更仔细地阅读这个问题。它不询问如何调用匹配器。它询问如何编写匹配器。
        • 这就是为什么我用粗体字突出显示该注释!
        • 虽然这个问题可以更好地表述,但我认为它并不难理解。你想让我删除哪一票?
        【解决方案5】:

        首先,您将正则表达式定义为: var re = new RegExp(/^(这里是正则表达式)$/);

        在 onKeypress 事件中,您可以像这样检查正则表达式:

        text.match(regexp) - 其中文本是输入的字符串。

        这清楚吗?

        【讨论】:

        • 你应该更仔细地阅读这个问题。它没有询问如何调用匹配器。它询问如何编写匹配器。
        猜你喜欢
        • 1970-01-01
        • 2011-12-16
        • 2016-04-19
        • 1970-01-01
        • 1970-01-01
        • 2019-10-11
        • 2017-10-30
        • 1970-01-01
        • 2014-09-10
        相关资源
        最近更新 更多