Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 为给定正则表达式创建所有可能匹配项的集合_Php_Regex_Algorithm_Language Agnostic_Dfa - Fatal编程技术网

Php 为给定正则表达式创建所有可能匹配项的集合

Php 为给定正则表达式创建所有可能匹配项的集合,php,regex,algorithm,language-agnostic,dfa,Php,Regex,Algorithm,Language Agnostic,Dfa,我想知道如何找到一组给定正则表达式的所有匹配项,这些匹配项的数量是有限的。 例如: 所有这些示例都可以假设它们以^开头,以$结尾 我还想知道是否有一种方法可以检索正则表达式的count a唯一a解,或者是否有一种方法可以确定正则表达式是否有有限解 如果该算法能够解析任何正则表达式就好了,但是一个足够强大的正则表达式子集就可以了 我对这个问题的PHP解决方案感兴趣,但其他语言也可以 编辑: 在我的正式理论课上,我学到了哪些可以用来实现正则表达式(和其他正则语言)。如果我可以将正则表达式转换为DFA

我想知道如何找到一组给定正则表达式的所有匹配项,这些匹配项的数量是有限的。

例如:

所有这些示例都可以假设它们以
^
开头,以
$
结尾

我还想知道是否有一种方法可以检索正则表达式的count a唯一a解,或者是否有一种方法可以确定正则表达式是否有有限解

如果该算法能够解析任何正则表达式就好了,但是一个足够强大的正则表达式子集就可以了

我对这个问题的PHP解决方案感兴趣,但其他语言也可以

编辑:

在我的正式理论课上,我学到了哪些可以用来实现正则表达式(和其他正则语言)。如果我可以将正则表达式转换为DFA,那么解决方案对我来说似乎相当简单,但这种转换对我来说似乎相当棘手

编辑2:


感谢所有的建议,我正在努力“回答”这个问题。

从正则表达式到DFA的转换非常简单。不过,您将遇到的问题是,生成的DFA可能包含循环(例如,for
*
+
),这将使其无法完全扩展。此外,
{n,n}
不能在DFA中清晰地表示,因为DFA没有循环次数的“内存”

这个问题的解决方案可以归结为构建一个函数,该函数对正则表达式进行标记和解析,然后返回一个包含所有可能匹配项的数组。在这里使用递归将帮助您获得大量的信息

伪代码中的起点可能如下所示:

to GenerateSolutionsFor(regex):
    solutions = [""]
    for token in TokenizeRegex(regex):
        if token.isConstantString:
            for sol in solutions: sol.append(token.string)
        else if token.isLeftParen:
            subregex = get content until matching right paren
            subsols = GenerateSolutionsFor(subregex)
            for sol in solutions:
                for subsol in subsols:
                    sol.append(subsol)
        else if token.isVerticalBar:
            solutions.add(GenerateSolutionsFor(rest of the regex))
        else if token.isLeftBrace:
            ...
我想知道如何找到一组给定正则表达式的所有匹配项,这些匹配项的数量是有限的

因为您只考虑表示有限语言的正则表达式,所以实际上您考虑的是字母表上正则表达式的子集。特别是,您没有处理使用Kleene星形运算符构造的正则表达式。这提出了一种简单的递归算法,用于构造字母∑上不带Kleene星的正则表达式表示的字符串集

LANG(a)     = {a} for all a ∈ Σ
LANG(x ∪ y) = LANG(x) ∪ LANG(y)
LANG(xy)    = {vw : v ∈ LANG(x) ∧ w ∈ LANG(y)}
考虑一个正则表达式,比如
a(b∪ c) d
。这正是您的猫和狗示例的结构。执行该算法将正确确定正则表达式表示的语言:

LANG(a((b ∪ c)d)) = {xy : x ∈ LANG(a) ∧ y ∈ LANG((b ∪ c)d)}
                  = {xy : x ∈ {a} ∧ y ∈ {vw : v ∈ LANG(b ∪ c) ∧ w ∈ LANG{d}}}
                  = {ay : y ∈ {vw : v ∈ (LANG(b) ∪ LANG(c)) ∧ w ∈ {d}}}
                  = {ay : y ∈ {vd : v ∈ {b} ∪ {c}}
                  = {ay : y ∈ {vd : v ∈ {b,c}}}
                  = {ay : y ∈ {bd, cd}}
                  = {abd, acd}

您还询问是否有一种算法可以确定正则语言是否是有限的。该算法包括构造接受该语言的确定性有限自动机,然后确定转换图是否包含从开始状态到包含循环的最终状态的行走。注意,没有Kleene星构造的正则表达式子集表示有限语言。因为有限集的并集和并集是有限的,所以接下来是简单的归纳。

这可能无法回答您所有的问题/需要,但可能是一个很好的起点。不久前,我正在寻找一种自动生成与regexp匹配的数据的解决方案,我发现这个perl模块Parse::RandGen,Parse::RandGen::regexp非常适合我的需要:


您可能想看看这个正则表达式库,它解析正则表达式语法(尽管有点不同于perl标准),并可以从中构造DFA:

我已经开始研究一个新的正则表达式。它已经可以处理大多数示例,并给出有限正则表达式的解集

它目前通过了以下单元测试

<?php

class RegexCompiler_Tests_MatchTest extends PHPUnit_Framework_TestCase
{

    function dataProviderForTestSimpleRead()
    {
        return array(
            array( "^ab$", array( "ab" ) ),
            array( "^(ab)$", array( "ab" ) ),
            array( "^(ab|ba)$", array( "ab", "ba" ) ),
            array( "^(ab|(b|c)a)$", array( "ab", "ba", "ca" ) ),
            array( "^(ab|ba){0,2}$", array( "", "ab", "ba", "abab", "abba", "baab", "baba" ) ),
            array( "^(ab|ba){1,2}$", array( "ab", "ba", "abab", "abba", "baab", "baba" ) ),
            array( "^(ab|ba){2}$", array( "abab", "abba", "baab", "baba" ) ),
            array( "^hello?$", array( "hell", "hello" ) ),
            array( "^(0|1){3}$", array( "000", "001", "010", "011", "100", "101", "110", "111" ) ),
            array( "^[1-9][0-9]{0,1}$", array_map( function( $input ) { return (string)$input; }, range( 1, 99 ) ) ),
            array( '^\n$', array( "\n" ) ),
            array( '^\r$', array( "\r" ) ),
            array( '^\t$', array( "\t" ) ),
            array( '^[\\\\\\]a\\-]$', array( "\\", "]", "a", "-" ) ), //the regex is actually '^[\\\]a\-]$' after PHP string parsing
            array( '^[\\n-\\r]$', array( chr( 10 ), chr( 11 ), chr( 12 ), chr( 13 ) ) ),
        );
    }

    /**
     * @dataProvider dataProviderForTestSimpleRead
     */

    function testSimpleRead( $regex_string, $expected_matches_array )
    {
        $lexer = new RegexCompiler_Lexer();
        $actualy_matches_array = $lexer->lex( $regex_string )->getMatches();
        sort( $actualy_matches_array );
        sort( $expected_matches_array );
        $this->assertSame( $expected_matches_array, $actualy_matches_array );
    }

}

?>

好问题。我想这样做对单元测试非常有用。@drrcknlsn这是我的想法之一,我正在考虑使用它为MVC基于正则表达式的路由系统生成一个完整的缓存。您假设的是隐式锚。很容易显示匹配给定字符串的所有可能方法。例如,给定“Hello world”,模式
/hel+o?/i
匹配Hello、Hell和Hell。不过,这与生成不同。它是Java而不是PHP,但它会让您开始学习。再看看是哪一个<代码>语言不可知的
[即通用解决方案,适用于每种语言]或
php
[解决方案可以而且应该使用php工具]。另外:您是采用ascii还是unicode?对于unicode,regex
可能会有问题[太多可能性]这正是我所想的,但我不明白
TokenizeRegex
如何工作。对我来说,这似乎是整个问题中最难的部分。@KendallHopkins:根据答案的上下文,标记器只是为每个正则表达式符号构建的lexer。只要您不介意在regex分析工具中使用regex,任何lex工具都应该可以工作。只有当你想避免使用正则表达式时,它才显得困难。无论如何,最好使用实际实现所使用的。请参见示例。
TokenizeRegex
的一个有效实现就是将字符串拆分为单个字符。将可作为一个单元处理的普通字符的范围合并在一起将提高性能(例如,
“地狱”、“o”和“?”
),但这并不重要。@Kendall:请检查。它解释了如何将(更简单的)正则表达式语法转换成NFA,NFA可以转换成DFA;这是一篇很棒的文章,可能有助于思考它。答案很好,但你可能想澄清你所说的“检查周期”是什么意思。当然,包含循环的DFA图对于它所接受的语言是无限的是必要的,但这是不够的。你说得对。我将“检查循环”改为“确定是否存在从开始状态到包含循环的最终状态的行走”。后者是一个充分条件,前者只是必要的。谢谢
<?php

class RegexCompiler_Tests_MatchTest extends PHPUnit_Framework_TestCase
{

    function dataProviderForTestSimpleRead()
    {
        return array(
            array( "^ab$", array( "ab" ) ),
            array( "^(ab)$", array( "ab" ) ),
            array( "^(ab|ba)$", array( "ab", "ba" ) ),
            array( "^(ab|(b|c)a)$", array( "ab", "ba", "ca" ) ),
            array( "^(ab|ba){0,2}$", array( "", "ab", "ba", "abab", "abba", "baab", "baba" ) ),
            array( "^(ab|ba){1,2}$", array( "ab", "ba", "abab", "abba", "baab", "baba" ) ),
            array( "^(ab|ba){2}$", array( "abab", "abba", "baab", "baba" ) ),
            array( "^hello?$", array( "hell", "hello" ) ),
            array( "^(0|1){3}$", array( "000", "001", "010", "011", "100", "101", "110", "111" ) ),
            array( "^[1-9][0-9]{0,1}$", array_map( function( $input ) { return (string)$input; }, range( 1, 99 ) ) ),
            array( '^\n$', array( "\n" ) ),
            array( '^\r$', array( "\r" ) ),
            array( '^\t$', array( "\t" ) ),
            array( '^[\\\\\\]a\\-]$', array( "\\", "]", "a", "-" ) ), //the regex is actually '^[\\\]a\-]$' after PHP string parsing
            array( '^[\\n-\\r]$', array( chr( 10 ), chr( 11 ), chr( 12 ), chr( 13 ) ) ),
        );
    }

    /**
     * @dataProvider dataProviderForTestSimpleRead
     */

    function testSimpleRead( $regex_string, $expected_matches_array )
    {
        $lexer = new RegexCompiler_Lexer();
        $actualy_matches_array = $lexer->lex( $regex_string )->getMatches();
        sort( $actualy_matches_array );
        sort( $expected_matches_array );
        $this->assertSame( $expected_matches_array, $actualy_matches_array );
    }

}

?>