16 - 正则表达式
正则表达式(regular expression)用于匹配特定规则的字符,比如验证一个邮箱的地址是否正确,电话号码是否合法等等,具有很好的灵活性复用性。它不是JavaScript独有的东西,准确说它是一个标准。大部分的开发语言都有自己的正则的定义方式,但是规则基本一致。先介绍JavaScript中的正则表达式。
01 - 概述
两种定义正则的方式
- 字面量
1 var reg = /hello/; 2 var str= 'hello world!'; 3 reg.test(str); // true
- RegExp构造函数
构造函数定义的正则, 正则体为传递的参数字符串。所以可以用变量代替某一部分的内容。
1 var a = "world"; 2 var reg = new RegExp("hello "+a); 3 var str ="hello world!"; 4 console.log(reg.test(str));//true 5 console.log(str.match(reg));//hello world
基本匹配规则
正则表达式中的大部分字符都被认为是普通的字符(字面量字符),所以最基本的方式需要匹配的字符写入正则表达式中。
但是实际上正则的使用都是许多比较复杂的规则,大多数需要匹配的,不是某个准确的字符,可能是一个范围内的字符,或者特定的字符结构,特定数量、顺序等等,这些特点都能成为匹配的条件。
- 普通字符(字面量字符)
1 var str = 'hello world'; 2 var reg = /world/;// 匹配 world 3 str.replace(reg,'JavaScript'); // hello JavaScript
- 特殊字符(元字符):特殊字符在正则表达式中有特定的含义或者作用,不能视做普通字符。如果需要匹配的字符是特殊字符,需要进行转义。
- . 点字符:匹配除开回车,换行,分隔符之外的所有字符。
- ^ $ 位置字符:表示开始和结束的特殊字符。
- | 竖线符号:表示或者或者的关系
- * + ? 量词符号:量词符号(后面详细介绍)
- () { } [ ] 括号:划分特殊区域
- \ 反斜线:转义符号,转义符号之后的字符会被转义,有特殊意义的符号将会失去意义,同时也可以让某些普通字符赋予特殊意义。
- 匹配特殊符号
1 1 var str = '1.1'; 2 2 var reg = /1\.1/;// 匹配 1.1
- 预定义字符
- 预定义字符,表示预设的一些常用的字符集合,通过转义对应的字符来表示。
- 同时大小写代表不同的含义,小写代表该类型,大写为非该类型,例如:\d 为匹配所有单个的数字,\D为匹配所有的单个非数字。
- \d \D 单个的所有数字
- \s \S 空格
- \w \W 所有字符(字母 数字 _)
- \b \B 独立部分(空格 ^ $ -)
- 特殊字符
- \n 匹配换行键
- \r 匹配回车键
- \t 匹配制表符 tab
- 匹配特殊符号
02 - 字符集
字符集用于表示字符的集合正则中中括号 [ ] 表示,匹配的过程中只需要匹配其中的一个字符就符合匹配条件,并且可以表示一个区间内的所有字符。
普通字符集
1 var reg = /a[1m帅]b/;// 匹配 a 1或m或帅 b 2 'a帅b'.replace(reg,'amo')// amo 3 .replace(reg,'帅');// amo
范围字符集
范围字符集与普通字符集一样表示集合中的某个字符,但是可以用于表示一个区间内的连续字符,比如数字:0-9,字母:a-z ,中文区间:[\u4E00-\u9FA5]
1 var reg = /[a‐c1‐3]/;// 匹配 a到z 1到5之间的任意字符等同 /a|b|c|1|2|3/ 或 /[abc123]/ 2 reg.test('c');// true 3 reg.test('2');// true
非字符集
非字符集,代表的是除开字符集之内的所有字符,表示方式为在字符集的最前面加上 ^ 符号,如 [^a-b] 代表匹配所有不是a-b的字符,实际上就是前两种字符集取反的匹配规则,除开 - \ ^ 其他特殊字符都视为普通字符
1 var reg = /[^a‐c1‐3]/;// 匹配不是 a到z 1到5 之间的任意字符 2 reg.test('c');// false 3 reg.test('4');// true
03 - 量词符
量词符用来表示一个或者一组字符重复出现时,规定其重复出现的次数,这个次数可以是一个固定的次数或者是一个次数的范围
指定重复次数的区间
- 固定的次数
1 var reg = /amo帅{1}!/;// 匹配一个'帅' 2 reg.test('amo帅!');// true 3 reg.test('amo帅帅!');// false
- n~m 次
1 var reg = /amo帅{1,2}!/;// 匹配1~2个'帅' 2 reg.test('amo帅!');// true 3 reg.test('amo帅帅!');// true 4 reg.test('amo帅帅帅!');// false
- 不限次数
1 var reg = /amo帅{2,}!/;// 匹配2个及以上的'帅' 2 reg.test('amo帅!');// false 3 reg.test('amo帅帅!');// true 4 reg.test('amo帅帅帅!');// true
简写量词符号
正则中可以定义了特殊的量词符号用于表示几类常用的量词区间
- ? :表示字符出现一次或者零次,等同 {0,1}
- + :表示字符出现一次或者多次,等同 {1,}
- * :表示字符出现零次或者多次,等同 {0,}
1 /a?b/; 2 /a+b/; 3 /a*b/;
贪婪匹配模式与非贪婪匹配模式
当元字符 . 与量词结合的时候可能会发生不可思议的问题,因为 . 匹配的是任意字符,如果这时候规定的字符个数没有上限那么它将从头匹配到尾。也就是说当匹配的次数没有上限的时候,默认的匹配次数为匹配成功的最大数量,这时候我们称之为贪婪模式
- 贪婪模式下(匹配至多)
1 var reg = /<.+>/;// 匹配开始标签 2 var str= '<div>hello</div><p>world</p>'; 3 str.match(reg);// match 返回匹配成功的内容 '<div>hello</div><p>world</p>' 4 5 6 var str = "amo 帅帅帅"; 7 //贪婪模式(匹配至多) 8 var reg = /amo 帅*/; 9 str.match(reg);//amo 帅帅帅
- 非贪婪匹配模式(匹配至少)
1 var reg = /<.+?>/;// 匹配开始标签 2 var str= '<div>hello</div><p>world</p>'; 3 str.match(reg);// <div> 4 5 6 var str = "amo 帅帅帅"; 7 //非贪婪模式(匹配至少) 8 var reg = /amo 帅*?/; 9 str.match(reg);//amo
04 - 匹配组
正则中的括号表示一个匹配组,一般字符在匹配的时候字符之间都是独立的只有顺序关系,而匹配组的作用是把一组字符划分为一个整体作为一个匹配单位,这时候使用量词时会以组为单位进行匹配。同时匹配组中匹配的内容会被正则内部单独的进行存储提供给我们去使用。
匹配组对比非匹配组
1 var reg1 = /ab+c/;// 匹配 a开始 b连续 c结尾 2 var reg2 = /(ab)+c/;// 匹配 ab连续 c结尾 3 var str = 'abbc'; 4 reg1.test(str);// true 5 reg2.test(str);// false 6 reg2.test('ababc');// true
匹配组中的字符获取
匹配组的作用除了组合之外的一个重要的特性是,单独获取并存储匹配成功的某一部分字符,比如匹配标签的时候只想获取标签的名字。
获取的方式有很多种,不同的方法获取的方式可能都不尽相同,这里优先介绍通用的获取方式,其余的在相关方法中讲解
- \n :正则内直接引用括号匹配的内容,n是从1开始的自然数
1 // 匹配标签格式的字符 2 var reg = /<([^ ]*?).*?>.*?<\/\1>/; 3 reg.test('<div></div>');// true 4 reg.test('<div class="test">1111</div>');// true 5 reg.test('<div>1111</p>');// false
- RegExp.$n :通过RegExp方法引用上一次括号匹配的内容
1 // 匹配标签格式的字符 2 var reg = /<([^ ]*?).*?>.*?<\/\1>/; 3 reg.test('<div></div>'); 4 RegExp.$1 // div 5 reg.test('<p class="test">1111</p>'); 6 RegExp.$1 // p
05 - 标识符
标识符表示正则进行匹配的时候的一些额外的规则,分别有三个。字面量的方式是写在正则的最后 /hello/g ,构造函数为第二个参数 new RegExp('hello','g')
- g :默认情况下正则进行匹配的时候只会匹配一次,使用g标识则会匹配完全部字符
1 var reg1 = /a/; 2 var reg2 = /a/g; 3 var str = 'ababa'; 4 str.match(reg1);// ['a'] 5 str.match(reg2);// ['a','a','a']
- i :默认情况下匹配字母会区分大小写,i标识符则表示忽略大小写
1 var reg1 = /A/; 2 var reg2 = /A/i; 3 var str = 'a'; 4 reg1.test(str)// false 5 reg2.test(str)// true
- m :改变 ^$ 的特性,默认为整段字符串的开始与结尾,m标识符则以换行符 \n 为区别开始和结尾
1 var reg1 = /^b/; 2 var reg2 = /^b/m; 3 var str = 'a\nb'; 4 reg1.test(str)// false 5 reg2.test(str)// true
06 - 正则相关方法
正则实例方法
- reg.test(str) :检测参数字符串是否能够匹配实例正则,返回一个布尔值
1 var reg = /hello/; 2 var str = 'hello world'; 3 reg.test(str);// trule
- reg.exec(str) :如果匹配成功则返回一个数组,数组主要由 匹配组 中的内容组成的数组,匹配不成功则返回null
- 第1成员:为匹配成功的字符串
- 第2~n成员:为匹配组中的内容
- index:为匹配成功开始的序列号
- input:匹配的原字符
1 var reg1 = / ([^ ]+)/; 2 var reg2 = / ([^ ]+)/g; 3 var str = 'hello world amo'; 4 str.match(reg1);// [" world", "world", index: 5, input: "hello world amo"] 5 str.match(reg2);// [" world", " amo"]
字符串实例方法
- str.match(reg) :返回匹配成功的字符串的数组
如果正则不带 g 修饰符号的时候与exec非常类似,返回第一次匹配成功的字符串以及匹配组中的内容,和成功开始的序列号和原字符等,如果带了 g 修饰符则只会返回又匹配成功的内容组成的数组
1 var reg1 = / ([^ ]+)/; 2 var reg2 = / ([^ ]+)/g; 3 var str = 'hello world amo'; 4 str.match(reg1);// [" world", "world", index: 5, input: "hello world amo"] 5 str.match(reg2);// [" world", " amo"]
- str.split(reg) :按照匹配成功的字符切割字符串,返回一个数组
1 var reg = /\s|‐|\./; 2 var str = 'a b‐c.d'; 3 str.split(reg);// ['a','b','c','d'];
- str.search(reg) :返回第一次匹配成功的位置,匹配失败则返回 -1
var reg = /a/; var str = 'hello world amo'; str.search(reg);// 12
- str.replace(reg|str,str|callback) :替换匹配成功的内容
- 第一参数为正则时,如果有 g 修饰符那么将会替换所有匹配成功的字符,否则只替换第一个
1 var reg1 = /a/; 2 var reg2 = /a/g; 3 var str = 'amo amo'; 4 str.replace(reg1,'A');// 'Amo amo' 5 str.replace(reg2,'A');// 'Amo Amo'
- 第一参数为正则,第二参数为字符串时,可以用 $ 带指一些字符
-
$&:表示当前匹配的字符
-
$`:表示当前匹配的字符前面的所有字符
-
':表示当前匹配的字符后面的所有字符
-
$n:n是从1开始的自然数,代表匹配内容中对应的匹配组
1 var reg = /\s(=)\s/; 2 var str = 'amo = 帅'; 3 str.replace(reg,'$&$&$&');// 'amo = = = 帅' 4 str.replace(reg,'$`');// 'amoamo帅' 5 str.replace(reg,'$\'');// 'amo帅帅' 6 str.replace(reg,'$1$1$1');// 'amo===帅'
-
- 第一参数为正则,第二参数为回调函数时,回调函数的参数,从第二参数开始返回匹配组中的内容
1 var reg = /(a)(m)(o)/; 2 var str = 'amo帅'; 3 str.replace(reg,function(item,$1,$2,$3,index,input){ 4 console.log(item);// amo 5 console.log($1);// a 6 console.log($2);// m 7 console.log($3);// o 8 console.log(index);// 0 9 console.log(input);// amo帅 10 return $1+'‐'+$2+'‐'+$3; 11 });// 'a‐m‐o帅'
- 第一参数为正则时,如果有 g 修饰符那么将会替换所有匹配成功的字符,否则只替换第一个
07 - 非获取匹配组
这些匹配组与普通匹配组的区别在于不会被计入获取的匹配组之中,并且断言只会作为匹配条件不会消耗实际的字符,其他匹配规则和一般匹配组类似
- ?: :非捕获组,使用了此标记的匹配组不会被捕获,也就是匹配的内容不会被存储
1 var reg = /(?:http|https):\/\/(.+?)\/(.*|$)/g; 2 var str = 'http://tanzhou.com/index.html'; 3 reg.exec(str);// ["http://tanzhou.com/index.html", "tanzhou.com", "index.html"]
- ?= :正向先行断言,此匹配组中的字符满足条件的时候,匹配组前面的字符才会被匹配
1 var reg = /(\d)(?=(\d{3})+$)/g;// 经典的数值补逗号正则 2 var str = '12345678901'; 3 var str_ = str.replace(reg,'$1,');
- ?! :反向先行断言,此匹配组中的字符不满足条件的时候,匹配组前面的字符才会被匹配
1 var reg = /(amo)(?!不帅)/g; 2 var str = 'amo帅'; 3 var str2 = 'amo不帅'; 4 reg.test(str);// true 5 reg.test(str2);// false
- ?<= :正向后行断言,此匹配组中的字符满足条件的时候,匹配组后面的字符才会被匹配
1 var reg = /(?<=a)b/g; 2 var str = 'abcb'; 3 str.replace(reg,'B');// 'aBcb'
- ?<! :反向后行断言,此匹配组中的字符不满足条件的时候,匹配组后面的字符才会被匹配
1 var reg = /(?<!a)b/g; 2 var str = 'abcb'; 3 str.replace(reg,'B');// 'abcB'
08 - 正则实例
常用验证正则
1 var detection = { 2 /* 电话号码 3 1. 1开头 4 2. 第二位 ‐‐ 3 4 5 7 8 5 3. 11位 6 */ 7 pNum:/^1[34578]\d{9}$/, 8 /* 邮箱 9 1. xxxxxx@xx.xxx 10 2. 长度不限 11 */ 12 email:/^(\w|‐)+@[a‐zA‐Z0‐9]+\.(com|cn|net)$/, 13 /* 用户名 14 1. 只能包含 字母 数字 _ 中文 15 2. 长度 4~12 位 16 */ 17 name:/^(\w|[\u4E00‐\u9FA5]){4,12}$/, 18 /* 密码 19 1. 长度至少六位 单独用长度判断 20 2. 不能为纯数字 21 3. 构成为字母和特殊字符 22 */ 23 password:/[a‐zA‐Z._@!#$%^&*()‐+~=,><?/\|{}\[\]]+\d*|\d*[a‐zA‐Z._@!#$%^&*()‐+~=,><?/\|{}\ 24 [\]]+/, 25 26 /* qq号 27 1. 长度至少六位 28 2. 纯数字 29 3. 超过10位的时候不能大于4的数字开头 30 4. 不能以0开头 31 */ 32 qq:/^([1‐9]\d{5,8}|[1‐3]\d{9})$/, 33 /* 身份证号码 34 1. 不能以0开头 35 2. 年在1000~2018之间 36 3. 月份天数不能超过极限值 37 4. 总长度为18位 38 5. 末尾可以为x 39 */ 40 id:/^[1‐9]\d{5}(1\d{3}|20[01][0‐8])(0[1‐9]|1[0‐2])(0[1‐9]|[12][0‐9]|3[01])\d{3}(\d|x)$/i 41 };
多行超出省略号
1 function ellipsis(dom,line,str){ 2 str = str || '...'; 3 var lH = parseFloat(getComputedStyle(dom).lineHeight)*line; // 获取最大高度 4 var h,text; 5 var reg = new RegExp(str+'$'); 6 do{ 7 text = dom.innerHTML.replace(reg,''); // 替换结尾拼接的省略字符 8 dom.innerHTML = text.slice(0,text.length‐1)+str; // 拼接字符 9 h = dom.clientHeight; // 重新获取高度 10 }while(h>lH); 11 }
驼峰改写
1 var str = 'box‐left‐icon'; 2 var _str = str.replace(/(?:‐)(.{1})/g,function(item,$1){ 3 return $1.toUpperCase(); 4 }); 5 console.log(_str);