在javascript编程中,会时常用到正则表达式。因此,决定对正则表达式进行学习。
学习的目的主要是能够在编程中使用到它,因此,不要求精通,只需对其基本知识有所了解即可。学习材料主要来自正则表达式30分钟入门教程,作者deerchao。
从目录及速览全文后,我认为,对正则表达式的基本了解应该主要从字符、限定符、规则、语法这四方面进行归纳。通过限定符和元字符配合使用来确定匹配字符的次数,通过设定并遵守一些规则来达到更多的匹配方式,最后进阶使用语法来达到最高层的正则表达式匹配。
1.字符:正则表达式中的字符分为原意文本字符和元字符。确定的英文字母和数字在正则表达式中可以用其本身表示(愿意文本字符),而元字符是一些具有特殊意义的正则表达式特殊代码,常用的元字符有下:
- . 匹配除换行符以外的任意字符;
- \w 匹配字母或数字或下划线或汉字;
- \s 匹配任意的空白符;
- \d 匹配数字;
- \b 匹配字符串开始或结束;
- ^ 匹配字符串开始;
- $ 匹配字符串的结束;
举个例子,\b\d6\d\b表示的是匹配首字符为任意数字、中间字符为6、尾字符为任意数字的一个字符串,而\d6\d$同样也是匹配的这样一个字符串。由元字符会衍生出一个问题:元字符中有一些如.等是用特殊符号来表示的,那么在正则表达式中用什么来匹配它们自己呢?这就引出了转义的规则:在这些特殊符号前面使用“\”对其进行转义,就可以匹配它们自身了。那么“\”又用什么来匹配呢?还是用它自己进行转义:正则表达式中的“\”就是用来匹配“\”的。
2.限定符:有了字符并不能解决问题,比如,如果想要匹配一个12位长的任意数字,难道要使用“\d”12次,那位数再多一些怎么办呢?
于是限定符就起作用了。我的理解,限定符就是规定正则表达式中一个字符或一段字符要匹配的次数,如下,是一些常用的限定符:
- * 重复零次或更多次;
- + 重复一次或更多次;
- ? 重复零次或一次;
- {n,} 重复n次或更多次;
- {n,m} 重复n到m次;
- {n} 重复n次;
- *? 重复任意次,但尽可能少重复;
- +? 重复1次或更多次,但尽可能少重复;
- ?? 重复0次或1次,但尽可能少重复;
- {n,m}? 重复n到m次,但尽可能少重复;
- {n,}? 重复n次以上,但尽可能少重复;
于是,要匹配12位长的数字,只需“\d{12}”这样写就行了。从上面可以明显的看出,前五个和后五个以中间第六个为界限,可以归纳为两大类,前五个叫做贪婪匹配,后五个叫做懒惰匹配(为什么用两个贬义词来定义,一脸懵逼??)。
3.规则:有了字符和限定符还并不能解决问题,还缺少一些规则,这些规则使我们能够更便捷的使用正则表达式。比较常用的规则有:字符类、反义、分支条件、分组、和向后引用。以下一一解释:
字符类:如果只想匹配某几个确定的字母或数字,就需要使用到字符类。举个例子,如果我只想匹配一段字符串中是否有a、b、c这三个字母,那么只需“[abc]”这样表示就可以进行匹配了,而这正是一个字符类。
反义:有时候,我们需要的结果是不想字符串中有某个字符或是某段字符(既匹配不含这些字符的字符串),就需要用到反义规则了,常见的反义规则代码有下:
- \W 匹配任意不是字母,数字,下划线,汉字的字符;
- \S 匹配任意不是空白符的字符;
- \D 匹配任意非数字的字符;
- \B 匹配不是单词开头或结束的位置;
- [^x] 匹配除了x以外的任意字符;
- [^aeiou] 匹配除了aeiou这几个字母以外的任意字符;
分支条件:指的是有几种匹配方式,如果满足其中任意一种匹配方式都应该当成匹配。而分支条件的具体方法是把不同的匹配方式用|隔开。举个例子,0\d{2}-\d{8}|0\d{3}-\d{7}这个正则表达式指的是只要某段字符串能够满足0xx-xxxxxxxx或者0xxx-xxxxxxx两种形式中的任意一种(x代表数字),就可以被匹配。而分支条件需要遵守另外一条规则:正则表达式的匹配都是从左到右进行匹配,如果字符串满足了前面的匹配方式,就被前面匹配了,既使它(或者它内部某段字符串,亦或它加上前后某段字符串)能够满足后面的匹配方式,也不会再次被后面匹配。
分组:分组的规则主要是为了满足一些语法或是规则,具体方法是使用小括号将某段正则表达式包围起来,既对其进行了分组,在默认情况下,如果一段正则表达式中有几个组,那么他们的组号是从左至右从1开始依次增大的。\b(\w+)\d(\w+)\b这段正则表达式里,第一个(\w+)的组号为1,第二个(\w+)的组号为2。
而依赖分组规则,后向引用就起到了很大的作用。如:有这样一段表达式,\b(\w+)\b\s+(\w+)\b,从中可见,第一个括号的内容与第二个完全相同,此时,完全可以利用后项引用规则,用第一个组的组号来替代第二个组:\b(\w+)\b\s+\1\b。需要注意的是,利用组号来引用,一定要加上转义符,如“\1”。
4.语法:常用的语法也主要依赖于分组,如下:
- (exp) 匹配exp,并捕获文本到自动命名的组里;
- (?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp);
- (?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号;
- (?=exp) 匹配exp前面的位置;
- (?<=exp) 匹配exp后面的位置;
- (?!exp) 匹配后面跟的不是exp的位置;
- (?<!exp) 匹配前面不是exp的位置;
- (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读;
以上,前三个属于捕获,其后四个属于零宽断言,最后一个是注释。需要注意的是零宽断言,为什么叫零宽?因为它匹配的是一个位置,并不是字符;而断言指的是这个位置需要满足一定的条件。
以上就是正则表达式的大半基础知识了,除此之外,还有处理选项、平衡组(递归匹配)等需要了解。对于一些常规的js编程如表单验证等,利用上面的知识就足够了。
但是,只利用正则表达式来匹配字符串并不能完全解决问题,比如有时需要计算字符串的字节长度,而字符串中含有汉字的情况下,需要将汉字进行转码,形成unicode编码,再对编码用“\”来转义,才能正确匹配找到汉字,并计算字节数。