发表评论
正则表达式能用得熟感觉就不错了,再了解原理...
([1-9])|([1][0-2]) 这种好像会把12分开匹配
难道| 这个也有优先级问题
是这样的, 正则匹配引擎对"|"连接的或匹配。优先匹配前面的组。
10, 12 前的数字1已经符合了前一个组, 所以认为匹配([1-9])这个组.
#3楼 [
楼主]2006-04-22 11:48 |
@ang
那可是反过来,1已经在[1][0~2]这个组里面,应该认为匹配这个组,但1后面没有其他字符了,是不符合[1][0~2]的啊,为什么还可以匹配呢?
javascript:openScript(news/dismemo.asp?id=138",500,400)
请问这是什么错误??
谢谢了!
因为编译原理里正则表达式是这样解释的
[1][0-2]表示串接,结果是(1)(0|1|2)={10,11,12}
后面再 | 上[1-9]={1,2,3,4,5,6,7,8,9}
所以总的集合就是(1,2,3,4,5,6,7,8,9,10,11,12}符合题目要求
补充一句,编译器都是从左到右扫描的
如果先匹配1~9则匹配成功后就不做 | 后面的 10 ~12匹配部分了
只有调整顺序 先匹配10~12
此时如果只输入一个1 是匹配不上前面部分的,因为前面的集合是10~12 ,这个1会自动去匹配后面的 1~9
有没试过
([1-9]{1})|([1][0-2])
^(([1-9])|([1][0-2]))$
改成这个就可以了(加了约束,必须从开始到结束匹配整个字符串)。
对于|运算,正则表达式在处理逻辑运算的时候确实是有优先级(事实上几乎所有的运算处理的时候总有个先后顺序),并且返回最早匹配的运算结果。对于10-12,([1-9])|([1][0-2])匹配的时候都首先匹配了[1-9],因此匹配的结果都是十位数1。如果次序调换一下,就会首先匹配[1][0-2],那么匹配的结果就是两位数了。
其实正则表达式在执行的时候并没有什么错误,也并不是没有找到匹配结果,主要是RegularExpressionValidator控件要求匹配出的结果一定要和用户输入的字符串一样才算通过。比如输入12,([1-9])|([1][0-2])匹配出的结果是1,而"1" != "12",所以没有通过。
@Anders Liu (lover_P)
>@ang
> 那可是反过来,1已经在[1][0~2]这个组里面,应该认为匹配这个组,但1后面没
>有其他字符了,是不符合[1][0~2]的啊,为什么还可以匹配呢?
因为你写的那个表达式是贪婪匹配, 不符合时, 会回溯. 于是匹配([1-9]), 匹配成功
呵呵,你写错了吧
<script language="javascript" type="text/javascript">
var match = /^[0-9]|[1][0-2]$/.test('12');
window.alert(match);
</script>
结果为True
正则表达式都会被翻译成(最小)DFA来执行的,所以正则表达式中的许多运算先后顺序,在消除状态的时候都可能面目全非了。
#14楼 [
楼主]2006-04-23 21:42 |
@装配脑袋
嗯,这个解释没问题,可为啥呢?
目前流行的正则式解析引擎大致分两种,一种用dfa实现(有awk (most versions), egrep (most versions), flex, lex, MySQL, Procmail等等),另一种用nfa实现(GNU Emacs, Java, grep (most versions), less, more, .NET languages, PCRE library, Perl, PHP (pcre routines), Python, Ruby, sed (most versions), vi等等),其中nfa的又分几种,行为上大体类似
具体细节三言两语说不完,只好忽略一些,用例子说明用"[1-9]|1[0-2]"匹配"12"时的行为区别:
使用dfa的引擎:首先,将"[1-9]|1[0-2]"转化成一个确定有限自动机
字符集:ansi字符集或unicode字符集
态0:起始态,接受到'1'内部状态转到态1,接受到'2'-'9'时转到态s,否则到态f
态1:接受到'0'-'2'时到态s,否则转到态s
态s:结束态,成功
态f:结束态,失败
匹配12,很显然,状态转换为0->1->s,其中,1->s时接受'0'-'2'比接受其它字符或不接受字符优先,因此会匹配到整个"12"。广义的说,这也是"dfa引擎"的一个特点,尽可能多的匹配目标,也能看到,dfa的正则式引擎是不需要回滚匹配过程的
使用nfa的引擎:自动机理论告诉我们,nfa和dfa是等价的,即nfa总可以转换为dfa,然而这里转化到dfa则会失去引擎的一些功能(比如look-ahead、look-behind、back-reference等等),因此支持这些功能的引擎是不可能采用类似dfa方式实现的,即不可能通过表达式产生完全的状态转换图(虽然局部可以,但为了行为统一一般也不会这么做),匹配的过程大体是:
1、输入"12",其中'1'符合[1-9]的条件
2、匹配完成
可见当输入能够满足|的第一个子表达式"[0-9]"时,引擎就不会再去尝试第二个子表达式"1[0-2]"了,由此可反映出nfa引擎的一个特点,写在前面的alternation优先级更高
从此例中正则式引擎的行为看,它属于nfa引擎
——上述内容是结合《Mastering Regular Expressions》(by Jeffrey E. F. Friedl)所述知识后的个人理解
现在正好在学编译原理
对于NFA ,DFA这些很熟
不过对于某种语言里的正则表达式写法不是很了解
第一种写法读入1后就直接进入结束状态了
第二种写法读入1后还会尝试读下一个字符,如果是0-2则进入结束状态,如果没有字符也进入结束状态作为1
DFA/NFA概念上是完整串运行到接受状态中止,而实际的正则引擎常常是匹配到不能匹配的时候中止,所以造成了NFA和DFA实现的不同。
NFA的猜测和回溯能力是很弱的,只不过是靠同时处于多个状态来完成。这里就是问题所在。同时处于多个状态的NFA在实现的时候到底哪个状态先尝试下一个输入符号?在NFA概念上这些同时发生,所以NFA才和DFA一样,但是在实现代码的时候,很可能是两种情况,一种是某个分支尝试直到匹配失败或成功,第二种是每个分支都尝试1步,然后继续。没有写过正则引擎所以不知道常常是哪一种实现,但如果是某一个分支先尝试到底的话,很可能并运算写在|前面的那个分支(因为常见的文法分析器中二元运算运算是从左到右解析的),所以这样可能就造成了|前边部分先匹配的现象。
我把NFA和相应的DFA都画出来了,上面是带空转移的NFA,下面是DFA。从图上看,即使DFA也可以提前匹配1,而不继续搜索[0-2]。那就只有引擎实现的方法不同可以决定这里匹配的不同了。
哈哈,终于见识了什么才叫肯钻研的技术牛人啊!^_^,赞一个!
这只是子集构造出来的DFA,如果转化为最小DFA就更难判断了
最小DFA中p1和p3可以合并,也就是说只有三个状态p0, p1, p2(和一个代表死状态的f)
一下是最小DFA的转移:
p0 -> p1 [1-9]
p1 -> p2 [0-2]
p0 -> f [^1-9]
p1 -> f [^0-2]
p2 -> f .
f -> f.
这样一来,到达p1之后是继续匹配p2,还是接受(终止)就是一个大问题了。不知道DFA型引擎一般是设计为最长匹配还是贪心匹配。
呵呵,试试下面这个吧
^(([1-9])|([1][0-2]))$
这个问题跟正则引擎有关, 有些正则引擎实现的较差, 所以才会这样.
不知楼主在什么正则引擎下使用该正则.
Nuva 语言在以下两种情况下都是正确的:
<.
?? '10'.RegexIsMatch('([1-9])|([1][0-2])')
?? '10'.RegexIsMatch('([1][0-2])|([1-9])')
.>
执行结果为:
True
True