从一组逻辑表达式构建正则表达式

时间:2014-04-21 21:42:25

标签: php regex search

我为逻辑表达式创建了一个简单的语法,使用布尔运算符和字符串作为操作数,我计划将其作为URL查询字符串传递。我将根据$query字符串中的操作数是否存在于另一个字符串$text中来使用它来匹配字符串。

考虑以下示例(在PHP中):

function search($text, $query) // $text, $query are strings
{
   // returns true if logical expression built from $query is true for $test string
   // otherwise returns false. $query explained:
   // (1) variables in $query are strings between brackets and operators
   // (2) operators are: &=and, |=or, !=not 
   // (3) priority of logical operations is defined by brackets: ()
   // (4) variable = true if substring exists in $text, false if not
}

// examples:
$text1 = 'str4 str3 blalbablastr2';
$text2 = 'str4whateverhere str3str5';

// str5 is present or str1 is present
$simpleQuery  = 'str5|str1';

// (str1 or str2 present), (str3 present), and (str4 present or str5 not present)
$complexQuery = '(str1|str2)&str3&(str4|!str5)';

search($text1, $simpleQuery);  // returns false as 'str5' OR 'str1' = false
search($text2, $simpleQuery);  // returns true  as 'str5' = true
search($text1, $complexQuery); // returns true  as 'str2' AND 'str3' AND ('str4' AND NOT 'str5') = true
search($text2, $complexQuery); // returns false as 'str1' OR 'str2' = false

我可以使用“传统”方法编写搜索功能:

  • 提取子字符串并创建相关变量
  • init变量(根据它们在$ text中的存在分配true / false)
  • 构建操作树(优先级)
  • 将运算符应用于变量

但是我相信有更短的&更快的方法使用正则表达式。如果你知道正则表达式足以提供实际帮助,请帮忙。谢谢!

4 个答案:

答案 0 :(得分:0)

如果您只想匹配字符串,则可能不需要编写search函数。 preg_match会执行示例中的搜索功能。

如果你不理解正则表达式在http://www.regular-expressions.info/的工作方式,你可以找到它们。

答案 1 :(得分:0)

你正在寻找绕过“传统”方法的方法,但即使采用正则表达式方法,你仍然需要完成你勾勒出的大部分步骤:

  • 在构建正则表达式
  • 之前,您仍需要提取子字符串
  • 您仍需要确定正确的操作顺序

我认为如果你根本不使用你的语法,你只会从正则表达式方法中获得优势,并将你的过滤器存储为正则表达式。

以下是一些可以产生与过滤器相同结果的正则表达式的示例:

# simpleQuery
str5|str1

# complexQuery (with lookahead)
(?=.*(str1|str2))
(?=.*str3)
((?=.*str4)|(?!.*str5))

当您开始添加连词时(如complexQuery中所述),您最终必须明确涵盖我使用lookaheads处理的搜索字词的不同顺序。

在简单的语言中,complexQuery中的前瞻只是说明搜索字符串中存在一些位置:

  • 后跟任何以“str1”或“str2”
  • 结尾的字符
  • 后跟任何以“str3”
  • 结尾的字符
  • 或者:
    • 跟随“str4”
    • 终止的任意数量的字符
    • 未跟随“str5”
    • 终止的任意数量的字符

如果没有前瞻,complexQuery的前两行必须写成:

((str1|str2).*str3)|(str3.*(str1|str2))

当您添加更多连词时,第二种方法将变得非常笨拙。

正如你所看到的,正则表达式并不像你的语法那样漂亮,而且我认为你原来的方法可能是最好的。

答案 2 :(得分:0)

这就是我使用eval()解决自己问题的方法。 也许不是最好的解决方案(由于eval),但它确实有效。

注意:为了让生活更轻松,我将$ query中的子句与周围的引号区分开来。

class QueryText
{
/**
 * Parses $query and performs search in $text
 *
 * @param  string $query   query string, PHP syntax, search strings should be in commas
 * @param  string $text    content to searhc
 * @param  array $options
 *
 * @return mixed          true on success,
 *                        false on failure,
 *                        null if parsing error
 *
 */
public function search($query, $text, $options)
{
    $defaultOptions =
    [
        'caseSensitive' => false
    ];

    $options = array_merge($defaultOptions, $options);

    $text  = ($options['caseSensitive']) ? $text : mb_strtolower($text);
    $query = ($options['caseSensitive']) ? $query : mb_strtolower($query);

    if (!self::isValid($query, $text)) return null;

    $evalStr = self::parse($query, $text);

    // prepare for execution
    $evalStr = 'return ((' . $evalStr . ') ? 1 : 0);';
    $result = eval($evalStr);

    return ($result===false) ? null : (($result===1) ? true : false);
}

private function isValid($query, $text)
{
    // 1. check general syntax: should be any strings in quotes; left symbols should be &&, ||, (, ), !, spaces
    $regex = '/("[^"\']+")|(\'[^\'"]+\')|(&&)|(\|\|)|([!()\s])/';
    $str   = preg_replace($regex, '', $query);
    if ($str!=='') return false;

    // 2. check brackets: should be even
    $regex   = '/([()])/';
    $matches = [];
    preg_match_all($regex, $query, $matches);
    if (count($matches[0])%2 !== 0) return false;

    // 3. check parsed expression
    $regex   = '/(\|&)|(&\|)|(&\))|(\(&)|(\(\|)|(\|\))|(&&&)|(\|\|\|)|(!\))|(^&)|(^\|)|(^\))|(&$)|(\|$)|(!$)|(\($)/';
    $str     = self::parse($query, $text);
    $matches = [];
    preg_match_all($regex, $str, $matches);
    foreach($matches as $match)
        if (count($match)!==0) return false;

    return true;
}

private function parse($query, $text)
{
    // inject true/false instead of strings
    $regex = '/("[^"\']+")|(\'[^\'"]+\')/';
    $evalStr = preg_replace_callback(
        $regex,
        function($matches) use($text)
        {
            if (trim($matches[0])=='') return '';
            $str = $matches[0];
            $str = trim($str);          // remove spaces
            $str = substr($str, 1);     // remove left quote
            $str = substr($str, 0, -1); // remove right quote
            if (strpos(trim($text), $str) !== false) return 'true';
            return 'false';
        },
        $query);

    // remove spaces and return result
    $regex = '/([\s]+)/';
    return preg_replace($regex, '', $evalStr);
}
}

答案 3 :(得分:-2)

你可以看看这里 http://tr1.php.net/preg_match http://tr1.php.net/preg_match_all

if (preg_match("/php/i", "PHP is the web scripting language of choice.")) {
echo "A match was found.";
} else {
echo "A match was not found.";
}