最近发现JavaScript中的正则在某些地方的表现和其他语言或工具中的正则有些不同,比较另类.虽然你几乎不可能写出也几乎用不到下面我讲的这些正则,但是了解一下毕竟是好的.
本文中的代码示例都是在兼容ES5的JavaScript环境中执行的,也就是说,IE9之前版本,Fx4左右的版本,等,中的表现很有可能和我下面讲的不一样.
1.空字符类
不包含任何字符的字符类[]称之为空字符类(empty char class),我相信你没听别人这么叫过,因为在其他语言中,这种写法是非法的,所有的文档和教程都不会讲一种非法的语法.下面我演示一下其他语言或工具都是怎么报这个错的:
$echo | grep '[]' grep: Unmatched [ or [^ $echo | sed '/[]/' sed:-e 表达式 #1,字符 4:未终止的地址正则表达式 $echo | awk '/[]/' awk: cmd. line:1: /[]/ awk: cmd. line:1: ^ unterminated regexp awk: cmd. line:1: error: Unmatched [ or [^: /[]// $echo | perl -ne '/[]/' Unmatched [ in regex; marked by <-- HERE in m/[ <-- HERE ]/ at -e line 1. $echo | ruby -ne '/[]/' -e:1: empty char-class: /[]/ $python -c 'import re;re.match("[]","")' Traceback (most recent call last): File "<string>", line 1, in <module> File "E:\Python\lib\re.py", line 137, in match return _compile(pattern, flags).match(string) File "E:\Python\lib\re.py", line 244, in _compile raise error, v # invalid expression sre_constants.error: unexpected end of regular expression
而在JavaScript中,空字符类是合法的正则组成部分,不过它的效果是"永不匹配",也就是匹配什么都会失败.相当于一个空否定正向环视(empty negative lookahead)(?!)的效果:
js> "whatever\n".match(/[]/g) //空字符类,永不匹配 null js> "whatever\n".match(/(?!)/g) //空否定正向环视,永不匹配 null
很显然,这种东西在JavaScript中没什么用.
2.否定空字符类
不包含任何字符的否定字符类[^]称之为否定空字符类(negative empty char class)或者叫空否定字符类(empty negative char class),都可以,因为这个名词是我"自创"的,和上面讲的空字符类类似,这种写法在其他语言中也是非法的:
$echo | grep '[^]' grep: Unmatched [ or [^ $echo | sed '/[^]/' sed:-e 表达式 #1,字符 5:未终止的地址正则表达式 $echo | awk '/[^]/' awk: cmd. line:1: /[^]/ awk: cmd. line:1: ^ unterminated regexp awk: cmd. line:1: error: Unmatched [ or [^: /[^]// $echo | perl -ne '/[^]/' Unmatched [ in regex; marked by <-- HERE in m/[ <-- HERE ^]/ at -e line 1. $echo | ruby -ne '/[^]/' -e:1: empty char-class: /[^]/ $python -c 'import re;re.match("[^]","")' Traceback (most recent call last): File "<string>", line 1, in <module> File "E:\Python\lib\re.py", line 137, in match return _compile(pattern, flags).match(string) File "E:\Python\lib\re.py", line 244, in _compile raise error, v # invalid expression sre_constants.error: unexpected end of regular expression $
而在JavaScript中,否定空字符类是合法的正则组成部分,它的效果和空字符类的效果刚刚相反,它可以匹配任意的字符,包括换行符"\n" ,也就是说,等同于常见的[\s\S]和[\w\W] :
js> "whatever\n".match(/[^]/g) //否定空字符类,匹配任意字符 ["w", "h", "a", "t", "e", "v", "e", "r", "\n"] js> "whatever\n".match(/[\s\S]/g) //互补字符类,匹配任意字符 ["w", "h", "a", "t", "e", "v", "e", "r", "\n"]
需要注意的是,它不能称之为是"永匹配正则",因为字符类必须要有一个字符才可能匹配,如果目标字符串是空的,或者已经被左边的正则消耗完了,则匹配会失败,比如:
js> /abc[^]/.test("abc") //c后面没有字符了,匹配失败. false
想要了解真正的"永匹配正则",可以看看我以前翻译的一篇文章:"空"正则
3.[]]和[^]]
这个讲起来比较简单,就是:在Perl和其他一些linux命令的正则表达式中,字符类[]中如果包含了一个紧跟着左方括号的右方括号[]] ,则这个右方括号会被当作一个普通字符,即只能匹配"]",而在JavaScript中,这种正则会被识别成一个空字符类后跟一个右方括号,空字符类什么都不匹配.[^]]也类似:在JavaScript中,它匹配的是一个任意字符(否定空字符类)后跟一个右中括号,比如"a]","b]" ,而在其他语言中,匹配的是任何非]的字符.
$perl -e 'print "]" =~ /[]]/' 1 $js -e 'print(/[]]/.test("]"))' false $perl -e 'print "x" =~ /[^]]/' 1 $js -e 'print(/[^]]/.test("x"))' false
4.$锚点
有些初学者认为$匹配的是换行符"\n" ,这是大错特错的,$是一个零宽断言(zero-width assertion),它是不可能匹配到一个真正的字符的,它只能匹配一个位置.我要的讲的区别发生在非多行模式中:你也许会认为,在非多行模式中,$匹配的不就是最后一个字符后面的位置吗?实际上没那么简单,在其他大部分语言中,如果目标字符串中的最后一个字符是换行符"\n" ,则$还会匹配那个换行符之前的位置,也就是匹配了末尾的换行符左右两边的两个位置.很多语言中都有\Z和\z这两个表示法,如果你知道它们之间的区别,那你应该就明白了,在其他语言中(Perl,Python,php,Java,c#...),非多行模式下的$相当于\Z,而在JavaScript中,非多行模式下的$相当于\z(只会匹配最末尾的那个位置,不管最后一个字符是否是换行符).Ruby是个特例,因为它默认就是多行模式,多行模式下$会匹配每个换行符前面的位置,当然也会包括结尾处可能出现的那个换行符.余晟著的《正则指引》一书中也讲到了这几点.