瑞士军刀---正则表达式

简介

正则表达式,又称正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。

说人话就是:对字符串执行模式匹配的强大工具。

Javascript中的正则表达式相较于其他语言来说实现的并不完整,但仍然是十分强大的,能够帮助我们完成绝大多数模式匹配任务。

定义

在javascript中,正则表达式的定义和数组以及对象的定义一样有俩种方式。 一种是直接量,另一种是构造函数。

直接量

Javascript可以使用类似Perl的语法定义一个正则表达式

var r = /pattern/flags;

其中pattern是任何简单或复杂的正则表达式,flags是用以表达正则表达式行为的一个或者多个标识。

flags

  • g 表示全局(global)模式,即模式将被应用于所有字符串
  • i 表示不区分大小写
  • m 多行模式

举个栗子:

var reg1 = /ip/g; // 匹配字符串中所有'ip'的实例

var reg2 = /com/gi; // 匹配以'com'结尾的三个字符的组合,不区分大小写

使用构造函数

Javascript中正则表达式的构造函数为RegExp,即Regular Expression的缩写,它接收两个参数,第一个为要匹配的字符串模式,另一个是可选的标识位。

var reg = new RegExp('nice', 'g'); // 匹配字符串中所有的'nice'

注意:第一个参数是一个字符串nice,而不是正则表达式的直接量/nice/

双重转义

所有元字符都需要进行双重转义

所有元字符都需要进行双重转义

所有元字符都需要进行双重转义

使用构造函数定义正则表达式,由于构造函数的第一个参数必须是一个字符串,在匹配元字符时,需要双重转义。

//  匹配{user}
var reg1 = new RegExp('\{user\}', 'g'); // wrong

// 由于\在字符串中需要转义为\\,所以,如果要匹配{user},正则表达式需要写为
var reg1 = new RegExp('\\{user\\}', 'g');

基本概念总结

元字符

在正则表达式的模式中,有一些字符是有特殊含义的,被称为元字符,如果要匹配元字符,则必须对其进行转义,如果用构造函数,则要进行双重转义。

这些元字符分别是(一共14个):

{ ( [  \ ^ $ |  ? * + . ] ) }

直接量字符

字符 匹配
字母数字字符 自身
\0 查找 NUL 字符。
\n 查找换行符。
\f 查找换页符。
\r 查找回车符。
\t 查找制表符。
\v 查找垂直制表符。
\xxx 查找以八进制数 xxx 规定的字符。
\xdd 查找以十六进制数 dd 规定的字符。
\uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。

不管一个直接量字符字符代表何种含义,单个字符始终只匹配单个字符

字符类

字符 等价于 匹配
. [^\n\r] 查找单个字符,除了换行和行结束符。
\w [a-zA-Z_0-9] 任意ASCII单字字符,等价于[a-zA-Z0-9_]
\W [^a-zA-Z_0-9] 查找非单词字符。
\d [0-9] 查找数字。
\D [^0-9] 查找非数字字符。
\s [ \t\n\x0B\f\r] 查找空白字符。
\S [^ \t\n\x0B\f\r] 查找非空白字符。
[] 来表示单个字符有或的关系,如/[bc]/,匹配b或者c
^ 来表示不匹配后面跟着的字符,如/[^bc],/不匹配b或c
- 来表示一个范围,如/[a-z]/,匹配a-z的字母,可以和负向类结合/^0-9/
组合类: 以上几种类组合而成的字符类,如/a-z0-9\n/,匹配a-z的字母或者0-9的数字或者换行符

注意:在javascript类不能嵌套,就是不支持联合类和交叉类,/a-m[p-z]/和/a-m[^b-e]/在JavaScript中是非法的

锚字符

字符 含义
^ 匹配字符串的开头,在多行检索中,匹配一行的开头
$ 匹配字符串的结尾,在多行检索中,匹配一行的结尾
\b 匹配单词边界。
\B 匹配非单词边界。

量词

简单量词

常用量词 {m,n}等价形式 描述
{0,1} 最多出现1次,例如,do(es)? 匹配 do 或 does 中的 do
* {0,} 可能出现,也可能不出现,出现次数无上限,例如,zo* 匹配 z 和 zoo。
+ {1,} 最少出现1次,例如,zo+ 匹配 zo 和 zoo,但不匹配 z

在正则表达式概念中,称 +、*、?为常用量词,称{n,m}形式的为通用量词。

使用情景举例

**?的使用
**
情景:美式应用和英式语言的单词写法不同,比如traveler和traveller.

var str1 = 'traveler';
var str2 = 'traveller';
var pattern = /travell?er/;
console.log(pattern.test(str1));
console.log(str2.search(str2));

结果打印:
true
0

匹配各类标签(前端应用)

  • 匹配所以tag: /<[^>]+>/
  • 匹配open tag: /<[/>][>]*>/
  • 匹配close tag: /</[^>]+>/
  • 匹配self-closing tag: /[^>/]+/>/

复杂量词

  • 贪婪量词:先匹配整个字符串,如果不匹配则去掉最后一个字符再匹配,直到没有任何字符。它是从后向前匹配。所有简单量词都是贪婪的
  • 惰性量词:和贪婪量词相反,即先匹配第一个字符,不匹配则匹配第一第二个字符,直到最后整个字符串。所有简单量词后面加?就是惰性的了
  • 支配量词:只匹配整个字符串一次,不匹配就结束,也称最懒量词,所有简单量词后面加+就是支配的了,但是javascript不支持,所以它连出场的机会也没有了。

举个栗子(complex-quantifiers.js):

var re1 = /.*bbb/g;     //  定义贪婪量词
var re2 = /.*?bbb/g;    //  定义简单惰性
//  var re3 = /.*+bbb/g;//支配性,javascript不支持,nodejs报错
var  str='abbbaabbbaaabbb1234';

console.log(re1.test(str));  //true
console.log(re1.exec(str));  //null
console.log(str.match(re1));  //abbbaabbbaaabbbb

console.log(re2.test(str));  //true
console.log(re2.exec(str));  //aabbb
console.log(str.match(re2)); //abbb,aabbb,aaabbb

贪婪模式详解

分组

到目前为止,我们只能一个字符到匹配,虽然量词的出现,能帮助我们处理一排密紧密相连的同类型字符。但这是不够的,下面该轮到小括号出场了,中括号表示范围内选择,大括号表示重复次数。小括号允许我们重复多个字符。

举个栗子(group.js):

//分组+量词
console.log(/(dog){2}/.test("dogdog"))//true
//分组+范围
console.log("baddad".match(/([bd]ad?)*/))//baddad,dad
//分组+分组
console.log("mon and dad".match(/(mon( and dad)?)/))//mon and dad,mon and dad, and dad

优先级

优先级 符号 说明
最高 转义符
( )、(?: )、(?= )、[ ] 单元和字符类
*、+、?、{n}、{n,}、{m,n} 量词
^、$、\任何元字符、任何字符 描点
最低 | 替换,选择

参考加减乘除,先算括号内的记忆方式

RegExp对象

RegExp属性

global 属性 | ignoreCase 属性 | multiline 属性 | source 属性

  • global: 返回布尔值,该值指示使用正则表达式的 global 标志 (g) 的状态。 默认值为 false。 只读。
  • ignoreCase: 返回布尔值,该值指示在正则表达式中使用的 ignoreCase 标志 (i) 的状态。 默认值为 false。只读。
  • multiline: 返回布尔值,该值指示在正则表达式中使用的 multiline 标志 (m) 的状态。 默认值为 false。 只读。
  • source: 返回正则表达式模式的文本的一个副本。 只读。
  • lastIndex: 如果使用了全局模式,这个变量保存的是字符串中尝试下次匹配的偏移值。在exec和test中都会用到这个变量。 可写

RegExp方法

RegExp 对象有 3 个方法:test()、exec() 以及 compile()

RegExp.test(string)

test 方法检查字符串中是否存在某种模式,如果存在,则返回 true,否则返回 false。test 方法不修改全局 RegExp 对象的属性。

语法:

rgExp.test(str) 

//  参数
rgExp 必需。 包含正则表达式模式和适用标志的 Regular Expression 对象的实例。
str 必需。 将对其执行搜索的字符串。

栗子:

var str = 'huangge is an handsome man';

var reg1 = new RegExp( 'handsome', 'g' );
var result = reg1.test(str);
console.log('huangge is an handsome man: ' + result);
RegExp.exec(string)

exec 方法使用正则表达式模式对字符串执行需找匹配,如果成功,则返回表示匹配信息的数组,否则返回null。默认情况下,它返回第一次匹配的结果

语法:

rgExp.exec(str) 

//  参数
rgExp 必需。 包含正则表达式模式和适用标志的 Regular Expression 对象的实例。
str 必需。 对其执行搜索的 String 对象或字符串文本。

返回值

如果成功匹配,exec 方法返回一个数组,并且更新正则表达式对象的属性。返回的数组包括匹配的字符串作为第一个元素,紧接着一个元素对应一个成功匹配被捕获的字符串的捕获括号(capturing parenthesis)。

如果匹配失败,exec 方法将返回 null。

栗子(regexp-exec.js):

var reg= /\d{4}-\d{2}-\d{2}/g;
var str = 'aaa2015-08-11aaaaaabbss2015-08-22bbb';
var result = reg.exec(str);
console.log(result);


结果如下:

[ '2015-08-11',
  index: 3,
  input: 'aaa2015-08-11aaaaaabbss2015-08-22bbb' ]
RegExp.compile(string)

将正则表达式编译为内部格式,从而更快地执行。

语法:

rgExp.compile(pattern, [flags]) 

//  参数
rgExp 必需。 Regular Expression 对象的一个实例。 可以是变量名或文本。
pattern 必需。 一个字符串表达式,包含要编译的正则表达式模式
flags 可选。 可以组合使用的可用标志有:
    g(全局搜索出现的所有模式)
    i(忽略大小写)
    m(多行搜索)

compile 方法将 pattern 转换为内部的格式,从而执行得更快。 例如,这允许在循环中更有效地使用正则表达式。 当重复使用相同的表达式时,编译过的正则表达式使执行加速。 然而,如果正则表达式发生更改,则这种编译毫无益处。

String的正则方法

String.match(RegExp)

这个方法类似RegExp.exec(string),只是调换了RegExp和string的位置。 另一个区别就是,无论是否指定全局,RegExp.exec(string)只是返回单词匹配结果。 而string.match()会返回一个字符串数组,其中包含了各次匹配成功的文本

举个栗子(string-match.js):

var reg= /\d{4}-\d{2}-\d{2}/g;
var str = 'aaa2015-08-11aaaaaabbss2015-08-22bbb';
var result = str.match(reg);
console.log(result);

打印的结果为:

[ '2015-08-11', '2015-08-22' ]
String.search(RegEexp)

这个用来寻找某个正则表达式在字符串第一次匹配成功的位置,如果不成功,则返回-1

举个栗子(string-search.js):

var str1 = 'sssssaa111';
var str2 = 'sssssaaaaa';

var pattern = /\d/;
console.log ('st1  ' + str1.search(pattern));
console.log ('st2  ' + str2.search(pattern));

打印的结果为:

st1  7
st2  -1
String.replace(RegExp,replacement)

这个方法用来正则表达式替换。 将匹配到的文本替换为replacement。默认情况替换一次。如果设定了全局模式,则所有能匹配的文本都会替换。
举个栗子(string-replace.js):

var reg = /\d{4}-\d{2}-\d{2}/;
var str = '2015-08-11 2015-08-22';

console.log(str.replace(reg, 'Date:'));

打印的结果为:

ate: 2015-08-22
String.split(RegExp [,limit])

这个方法使用一个正则表达式切分字符串,正则表达式是否使用全局对结果没有影响
举个栗子(string-split.js):

var str = 'one two  three    four';
var pattern = /\s+/;
console.log(str.split(pattern))

打印的结果为:

[ 'one', 'two', 'three', 'four' ]

常用的正则表达式

  • 匹配中文字符的正则表达式: [\u4e00-\u9fa5]

  • 匹配双字节字符(包括汉字在内):[^\x00-\xff]
    评注:可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

  • 匹配空白行的正则表达式:\n\s*\r

  • 匹配HTML标记的正则表达式:<(\S?)[^>]>.?</\1>|<.? />

  • 匹配首尾空白字符的正则表达式:^\s|\s$

  • 匹配Email地址的正则表达式:\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*

  • 匹配网址URL的正则表达式:[a-zA-z]+://[^\s]*

  • 匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$

  • 匹配国内电话号码:\d{3}-\d{8}|\d{4}-\d{7}

  • 匹配腾讯QQ号:[1-9][0-9]{4,}

  • 匹配中国邮政编码:[1-9]\d{5}(?!\d)

  • 匹配身份证:\d{15}|\d{18}

  • 匹配ip地址:\d+.\d+.\d+.\d+

匹配特定数字

^[1-9]\d*$    //匹配正整数
^-[1-9]\d*$   //匹配负整数
^-?[1-9]\d*$   //匹配整数
^[1-9]\d*|0$  //匹配非负整数(正整数 + 0)
^-[1-9]\d*|0$   //匹配非正整数(负整数 + 0)
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$   //匹配正浮点数
^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$  //匹配负浮点数
^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$  //匹配浮点数
^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$   //匹配非负浮点数(正浮点数 + 0)
^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$  //匹配非正浮点数(负浮点数 + 0)

匹配特定字符串

^[A-Za-z]+$  //匹配由26个英文字母组成的字符串
^[A-Z]+$  //匹配由26个英文字母的大写组成的字符串
^[a-z]+$  //匹配由26个英文字母的小写组成的字符串
^[A-Za-z0-9]+$  //匹配由数字和26个英文字母组成的字符串
^\w+$  //匹配由数字、26个英文字母或者下划线组成的字符串
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容