高效算法,以匹配固定数量的正则表达式模式的大量字符串

时间:2012-09-03 07:11:29

标签: regex performance algorithm

上下文

在解析Web访问日志时,需要将机器人与人类用户代理分开。

我手动构建了一个列表,提取了类似bot的模式(例如包含 bot )。我有一个模式列表(约300项大)我实时验证传入的访问日志。随着时间的推移,这已经成为一个瓶颈,因为我的(低效)算法寻找任何模式;即使我们忽略了性能下降,这意味着对DoS的漏洞。

代码(java)

String userAgent;
List<Pattern> botPatterns;

    for (Pattern botPattern : botPatterns)
    {
        if (botPattern.matcher(userAgent).find())
        {
            return true;
        }
    }
    return false;

已经探讨了答案

使用WURFL

这可能是一个简单的解决方案,但结果有时是不准确的。现在想避免使用内置解决方案。

使用用户代理的缓存

通过在hashmap中存储用户代理字符串,可以缓存结果。但是,即使这会提高性能,也会增加使用随机生成的用户代理字符串进行简单DoS攻击的漏洞。

问题

是否有一种有效的算法可以将大量(N)字符串与固定数量的正则表达式(m)进行匹配?

编辑:基准回答

我使用实际数据以及随机生成的用户代理字符串对@stemm的答案进行了基准测试。我在10秒内运行测试,平均计算时间。 缓存是一个大小为5000的简单LFU ehcache

以下是我的结果:

模式循环

无缓存

真实数据:101us

随机数据:193us

带有缓存的

真实数据:4us

随机数据:203us

单一模式

无缓存

真实数据:100us

随机数据:160us

带有缓存的

真实数据:3us

随机数据:154us

结论

  • 对于现实生活中的情况,缓存是最有效的解决方案。
  • 与循环模式相比,使用单一模式可获得25%的增益。
  • 缓存不像我想象的那样容易受到DoS的攻击。<​​/ li>

感谢大家的回答和评论。

2 个答案:

答案 0 :(得分:1)

如果你有固定数量的模式,你可以构建Finite State Machine来表示你的模式,这可以大大提高性能。但是这个解决方案需要额外的逻辑开发和代表FSM。

示例您有多个模式[abc,abd,acd,dab]

有限状态机看起来像:

start -> a -> b -> c -> finish
                -> d -> finish
           -> c -> d -> finish
      -> d -> a -> b -> finish

您可以将FSM视为状态有限的有序图。您希望在这些模式上匹配字符串abd。从一开始就遇到一个 - &gt; b - &gt; d并完成。这意味着字符串匹配您可以从路径还原的模式。具有*?等功能的更复杂模式可以表示链接状态与自身(*),或者完全跳过此状态(?)。

答案 1 :(得分:1)

主要想法是创建聚集模式,其中包含您拥有的所有正则表达式。

例如 - 您希望找到与这些正则表达式相匹配的字符串:^\d+$^[aA]+$^[a-z\s]+$。因此,您可以将它们组合成这样的正则表达式:(^\d+$|^[aA]+$|^[a-z\s]+$)

示例java代码:

public class Test {

    public static Pattern gather( String... patterns ) {
        StringBuilder finalRegexBuilder = new StringBuilder( "(" );
        for ( String ptr : patterns ) {
            finalRegexBuilder.append( ptr ).append( "|" );
        }
        finalRegexBuilder.setLength( finalRegexBuilder.length() - 1 );
        finalRegexBuilder.append( ")" );
        return Pattern.compile( finalRegexBuilder.toString() );
    }

    public static void main( String[] args ) {
        Pattern regex = gather( "^\\d+$", "^[aA]+$", "^[a-z\\s]+$" );

        System.out.println( regex.toString() ); // (^\d+$|^[aA]+$|^[a-z\s]+$)

        System.out.println( regex.matcher( "1235" ).find() ); // true
        System.out.println( regex.matcher( "1235fdjg" ).find() ); // false
        System.out.println( regex.matcher( "AAAaaaAaa" ).find() ); // true
        System.out.println( regex.matcher( "hello world" ).find() ); // true
    }
}

实际上 - 正则表达式的底层机制是有限状态机。所以,在上面的代码中我们得到了@mishadoff谈到的FSM。