如果正则表达式包含单个不变的段,则说明

时间:2012-02-15 20:18:19

标签: regex

我编写了一个优化的搜索工具,首先在句子中查找固定的字符短语 - 认为它很简单“句子是否包含特定的字符序列”。结果将是一组已找到的句子,可以在第二阶段进一步搜索。

对于第二阶段,我喜欢使用正则表达式搜索以方便起见。但我需要在第一阶段首先预先选择项目,我不能简单地得到所有句子 - 我需要在第一阶段使用的API要求我搜索至少一个匹配字符的短语。所以没有办法解决这个问题。

现在,用户只会输入一个正则表达式,我的软件需要首先确定它是否可以执行第一阶段搜索。如果用户输入含糊不清的内容,我会告诉用户更改他的正则表达式。

我需要算法来确定我可以用于第一阶段搜索的所有子串。

以下是预期结果的一些示例:

  • a.b - 是(首先搜索“a”或“b”)
  • a | b - 否(必须进行两次不同的第一级搜索)
  • [ab] - 否(同样的问题:第一阶段搜索不是明确的目标)
  • [ab] c - 是(首先搜索“c”)

这些是简单的例子。但是由于正则表达式会变得非常复杂,我想知道我是否可以构建一个正则表达式或其他测试,告诉我是否有可用的结果。 我还可以将正则表达式语法限制为更常见的情况,如果这使得测试更简单,例如没有递归或任何可能有帮助的东西。

1 个答案:

答案 0 :(得分:1)

这是一个使用Haskell的例子。该算法应该很容易转移到另一种语言。

只是一些样板导入;

import Data.Maybe
import Data.List
import Data.Function

这是表示正则表达式的数据类型。您必须自己解析它或使用库:

data Regex
    = Concat Regex Regex -- e.g. /ab/
    | Alt Regex Regex    -- e.g. /a|b/
    | Single Char        -- e.g. /a/
    | Star Regex         -- e.g. /a*/
    | CharClass [(Char,Char)] -- a list of ranges. for non-range (e.g. [a]) just use the same char twice

以下是算法:

regexMustMatch (Single x)    = [Just x]   -- has to match the character
regexMustMatch (Alt _ _)     = [Nothing]  -- doesn't need to match one thing (you could actually check for equality here, so something like /a|a/ would work)
regexMustMatch (Star _)      = [Nothing]  -- doesn't need to match one thing 
regexMustMatch (CharClass ((a,b):[])) | a == b = [Just a] -- char class must match if it only has one character
regexMustMatch (CharClass _) = [Nothing]                  -- otherwise doesn't need to match one thing 
regexMustMatch (Concat x y)  = (regexMustMatch x) ++ (regexMustMatch y) -- must match both parts in sequence

使结果可用的一些方法:

selectAll = map (concatMap (return . fromJust)) .
            filter (isJust . head) .
            groupBy ((==) `on` isJust)

selectLongest x = case selectAll x of
        [] -> ""
        xs -> maximumBy (compare `on` length) xs

还有一些例子:

main = do
    -- your tests
    -- /ab/
    print . selectAll . regexMustMatch $ (Single 'a' `Concat` Single 'b')
    -- /a|b/
    print . selectAll . regexMustMatch $ (Single 'a' `Alt` Single 'b')
    -- /[ab]/
    print . selectAll . regexMustMatch $ (CharClass [('a','a'),('b','b')])
    -- /[ab]c/
    print . selectAll . regexMustMatch $ ((Single 'a' `Alt` Single 'b') `Concat` Single 'c')

    -- a few more
    -- /[a]/
    print . selectAll . regexMustMatch $ (CharClass [('a','a')])
    -- /ab*c/
    print . selectAll . regexMustMatch $ (Single 'a' `Concat` Star (Single 'b') `Concat` Single 'c')
    -- /s(ab*)(cd)/ - these aren't capturing parens, just grouping to test associativity
    print . selectAll . regexMustMatch $ (Single 's' `Concat` (Single 'a' `Concat` Star (Single 'b')) `Concat` (Single 'c' `Concat` Single 'd'))

输出:

["ab"]      -- /ab/
[]          -- /a|b/
[]          -- /[ab]/
["c"]       -- /[ab]c/
["a"]       -- /[a]/
["a","c"]   -- /ab*c/
["sa","cd"] -- /s(ab*)(cd)/

可以改进的主要区域是交替算法。

如果我们有正则表达式/a*bc*|d*be*/,那么b需要匹配,但这不会选择它。

编辑:这是一种改进的替代算法:

regexMustMatch (Alt x y)
    | x' == y' = x'
    | otherwise = start ++ [Nothing] ++ common ++ [Nothing] ++ end
    where
        x' = regexMustMatch x
        y' = regexMustMatch y
        start = map fst $ takeWhile (uncurry (==)) (zip x' y')
        end = map fst $ reverse $ takeWhile (uncurry (==)) (zip (reverse (drop (length start) x')) (reverse (drop (length start) y')))
        dropEnds = drop (length start) . reverse . drop (length end) . reverse
        common = intercalate [Nothing] $ map (map Just) (selectAll (dropEnds x') `intersect` selectAll (dropEnds y'))

改进后的更多测试:

/a*bc*|d*be*/    == b
/s(abc*|abe*)/   == sab
/s(a*bc*|d*be*)/ == s, b
/sa*b|b*/        == s
/(abc*|abe*)s/   == ab, s
/(a*bc*|d*be*)s/ == b, s
/(a*b|b*)s/      == s
/s(ab|b)e/       == s, be
/s(ba|b)e/       == sb, e
/s(b|b)e/        == sbe
/s(ac*b|ac*b)e/  == sa, be