回溯

小心别落入正则回溯陷阱

99封情书 提交于 2020-02-29 14:32:50
不知才哪儿看来的: 如果你有一个问题,你想到可以用正则来解决,那么你有两个问题了。 回溯 对于正则而言,回溯并不是必需的,这跟具体的正则引擎有关。简单地说,正则引擎分为NFA和DFA。这东西难懂且无聊,我就挑重点说。DFA(确定型有穷自动机),从匹配文本入手,从左到右,每个字符不会匹配两次,它的时间复杂度是多项式的,所以通常情况下,它的速度更快,但支持的特性很少,不支持捕获组、各种引用等等;而NFA(非确定型有穷自动机)则是从正则表达式入手,不断读入字符,尝试是否匹配当前正则,不匹配则吐出字符重新尝试,通常它的速度比较慢,最优时间复杂度为多项式的,最差情况为指数级的。但NFA支持更多的特性,因而绝大多数编程场景下(包括js),我们面对的是NFA。 NFA匹配的过程就是吃入字符,尝试匹配,如果通过,再吃入尝试;如果不通过,就吐出,回到上一个状态,因为同一个字符串在正则中可能存在一种状态不同转化路径,这时正则引擎换一个转化状态进行尝试,如果通过,继续吃入字符,否则继续吐出字符,回到再上一个状态。这种尝试不成功就返回上一状态的过程,我们称为回溯。正则匹配的性能好坏,就看回溯的情况,回溯越多,性能越差。 为了说清楚这个问题,我们做一个实验,用 a(acd|bsc|bcd) 这个正则来对“abcd”这个字符串进行匹配。 截图上方是正则表达式,右侧是要匹配的文本,左侧是匹配的过程。 可以看到

Java正则表达式通过回溯与前后查找提取标签中的内容

泪湿孤枕 提交于 2019-12-10 05:22:28
在说具体的内容之前先来了解一下什么是向前查找,向后查找与回溯。我们通过一下小的例子来理解一下。具体的代码附录在最后。 ##向前查找 Java中向前查找的正则表达式是(?=),更加具体的就是 要匹配内容的正则(?=匹配的边界的正则) 。来一个具体的例子,比如我们有一个具体的字符串 2016年:鸡翅 15元 鸡腿 10元 鸡爪 5元 ,现在我们要取出里面的价格15 10 5,而不要2016,显然我们不能直接\d+来匹配。 我们很容易发现规律,我们需要提取单位‘元’前面的数字,但是我们不想要单位‘元’,怎么办,这里就可以使用向前查找了。 content = "2016年:鸡翅 15元 鸡腿 10元 鸡爪 5元"; //正则表示,匹配元前面的数字,以元作为数字的后边界但是不匹配‘元’ regex = "\\d+(?=元)"; 匹配的结果就是15 10 5,不包含2016,后面有代码可以自己测试一下。 ##向后查找 首先向后查找的正则表达式是(?<=),更加具体的就是*(?<=匹配边界的正则)要匹配内容的正则*,向后查找和向前查找一样,只不过是匹配边界后面的内容。下面还是通过一个具体的例子来说明。比如我有一个url字符串: http://www.freemethod.cn,我想提取出www.freemethod.cn。要真么做了。这里就可以使用向后匹配。当然你说我有一万种方法可以完成这个事情