正则表达式和它在前端的应用

序言

正则表达式,又叫规则表达式。把人类世界的一些字符规则以计算机能够理解的语言表达出来。Javascript提供了一个对象RegExp(Regular Expression)来管理和正则表达式相关的一切。

 

创建方式

有两种声明正则对象的方法:

  • 字面形式创建:reg = /pattern/attributes
  • new形式创建:reg = new RegExp(pattern, attributes)    // 简写方式更常用

其中,attributes代表该正则的属性参数,可选值为g, i, m,分别代表全局匹配,忽略大小写,换行匹配。其中m在ECMAScript标准化之前不支持使用。

书写规则

常用元字符

常用的正则匹配字符按功能可以分为“匹配字符”“匹配位置”和“量词”。

字符:

  • .:匹配处换行符以外的任意字符
  • \w:匹配字母,数字,下划线或汉字(word)
  • \s:匹配任意空格字符(空格,回车,Tab等)(space)
  • \d:匹配任意数字(digital)

位置:

  • \b:匹配单词首尾(单词分隔符)
  • ^:匹配输入字符串的开始
  • $:匹配输入字符串的结束(在编写校验的时候必须加行首行尾)

量词:

  • {n,m}:重复n~m次
  • *:重复零次或更多次,与{0,}相同
  • +:重复一次或更多次,与{1,}相同
  • ?:重复0次或1次,与{0,1}相同

字符类

要想查找数字,字母或数字是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?

很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。

我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。此外,最常用的还有检测输入是否含有中文,使用[\u4e00-\u9fa5]。

字符类还可以用[^a]来匹配任何除了a以外的字符。

分支条件

正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。

\d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

反义

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:

  • \W:匹配字母、数字、下划线、汉字以外的字符 ([^a-zA-Z0-9])
  • \S:匹配不是空格字符的字符
  • \D:匹配任何非数字的字符
  • \B:匹配不是单词开头或结束的位置
  • [^x]:匹配除x以外的字符
  • [^abcde]:匹配除abcde以外的字符

后向引用

如果我们需要重复匹配多个字符,可以用小括号将需要的部分括起,指定子表达式(分组)。使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

比如我们要匹配类似go go或kitty kitty的重复出现的字符,就可以使用以下匹配方式:\b(\w+)\b\s+\1\b。

 

常用的分组语法如下:

  • (exp):匹配exp,并对捕获文本自动命名为\1,\2...
  • (?<name>exp):匹配exp,将捕获问文本以name命名,通过\k<name>来引用该匹捕获文本
  • (?:exp):匹配exp,不捕获匹配的文本,也不给此分组分配组号

由第二条,我们可以得到上面正则的另一种表现形式:\b(?<Word>\w+)\b\s+\k<Word>\b。

零宽断言

零宽断言用于查找在前面或后面满足某种条件的字段(但不包括前后满足匹配的那部分),也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。

(?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。

(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。

注:JS引擎不支持这种正则表达方式。因为这种方式效率不高,推荐使用分组进行匹配。

负向零宽断言

负向零宽断言用于查找在前面或后面不满足某种条件的字段(但不包括前后满足匹配的那部分)。此处要与反义字符做区分。例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:

\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。

零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。

这有一个用于匹配没有内联样式的HTML标签中的内容(不匹配标签):(?<=<(\w+)>).*(?=<\/\1>)。这个表达式最能表现零宽断言的真正用途。

注释

小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。

贪婪匹配与懒惰匹配

正则有一个特点——贪婪。它会匹配尽可能多的东西。比如当我想将<div><bold>标题</bold>文本</div>中的标签用空格替换掉,如果使用/<.+>/则会使整段文本被替换,因为正则会匹配尽可能多的字符。那么应该如何让正则匹配尽可能少的字符呢?这时候就需要使用“懒惰匹配”。只需要在量词(*+[2,4])后面加一个问号,就可以让正则匹配尽可能少的字符。针对上面的例子就应该使用/<.+?>/

ps: 本例也可以用/<[^<>]+>/来实现。

 

常用方法

RegExp对象的方法

  • regexp.test(string):用来检测传入字符串中是否有正则匹配项,如果有,返回true,否则返回false
  • regexp.exec(string, 'g'):匹配正则表达式并且返回匹配的字段数组。

String对象的方法

0. 常见的字符串操作

var str = 'abcdef'; 

str.search('b'); // 1
str.substring(1,4)  //"bcd"
str.charAt(0);  //"a"

var str = 'abc-12-u-qw';
var arr = str.split('-');
alert(arr);  //["abc", "12", "a", "qu"]

1. 字符串的正则(规则)操作

var str = 'abc 12 as23 1';

str.search(/\d/);  //4
str.match(/\d+/g);  //["12", "23", "1"]
str.replace(/a/g, 'T');    // 'Tbc 12 Ts23 1'

ps: replace()与正则表达式应用实例——过滤敏感词与提取HTML标签内的纯文本

match()和exec()的不同 

在非全局匹配时,str.match(reg)和reg.exec(str)都能实现分组匹配,然而当进行全局匹配时,exec能够记录上次匹配的索引,继续进行匹配。此时通常结合循环使用,事例如下:

var s = 'aaalllsss0tAAAnnn999';
var re1 = /((\w)\2{2})(\w)\3{2}/g;
var res;

while(res=re1.exec(s)) {
  console.log("match result: " + res[1]};
  console.log("re1.lastindex: " + re1.lastIndex);
  console.log("remain string: " + s.slice(re1.lastIndex));
}

?的四种用法

 

  1. 在表示正常字符时,需进行转义\?
  2. 表示数量,0或1
  3. 放在量词后,表示去贪婪匹配,即匹配最小数量
  4. 放在括号内,表示不捕捉模式(?:exp)

参考文章

正则表达式30分钟入门教程

posted @ 2015-12-18 16:56  潘诗瑶  阅读(389)  评论(0编辑  收藏  举报