正则表达式
概念
什么是正则表达式?
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
正则表达式通常用来检索,替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母,数字或者下划线,昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等。
特点
- 灵活性,逻辑性,和功能性非常的强
- 可以迅速地用既简单的方式达到字符串的复杂控制
- 对于刚接触的人来说,比较晦涩难懂
- 实际开发,一般都是直接复制写好的正则表达式,但是要求会使用正则表达式并且根据实际情况修改正则表达式
转义 -> 转换意义/改变意义
- 转义符号:
\ - 转义字符:
\字符,改变原来的功能
// 打印字符串里的双引号写法var str = '我是一名"牛逼"的程序员';
问题:为什么出现语法错误?
答案:因为中间的字符是变量,需要加号才能拼接在一起
程序是如何识别?
JavaScript对待语法来说,先解析正常的语法,分析完才做文本输出,此时发现变量与字符串中间没有加号,此时会报语法错误
var str = "我是一名"牛逼"的程序员";
如何把原来的解析规则(字符串后引号)改为单纯的字符意义?(双引号的功能变为字符)
此时需要转义,让字符本来的功能变为另外的意义和功能
var str = "我是一名\"牛逼\"的程序员";
如何只显示\?
var str = "我是一名\\牛逼\\的程序员";
转义字符
特殊的转义字符:
\n:换行\r:回车\t:制表符
注意:
\n只给编辑系统使用,在页码不显示换行,只显示空格(占用了字符),原因HTML是纯文本而不是编辑系统- 如windows编辑系统回车键默认加
\r,\n- 如mac编辑系统回车默认加
\r- 如linux编辑系统回车默认加
\n- 制表符:table的缩写tab 相当于4个空格
问题:为什么换行会报错?
var str = '<h1>11111</h1><h2>22222</h2><h3>33333</h3>';
原因:JavaScript默认不允许字符串存在多行的情况
var str = '<h1>11111</h1>\<h2>22222</h2>\<h3>333333</h3>';
转义为多个空格
描述
RegExp - regular expression
按照一定的规则匹配或检索这个规则当中指定的或指定类型的某些字符,或者是一些特殊的字符的方法
匹配规则则是正则表达式,对字符串操作的一种逻辑公式,对某一些字符串中的某一些字符进行检索,但逻辑需要一种方式 去写成一种规则,然后让程序调用这种规则。
用途
- 检测输入(邮箱)
- 密码是否带有某些字符
- 替换数据中的某些字符串
- 替换模板
正则也是一个对象,也可以实例化一个正则
var reg = new RegExp(正则表达式);
参数1:为正则表达式
以这个正则表达式为规则,来检验一下,str里是否包含test,如果包含为true
var str = 'This is a test';console.log(new RegExp('test')); // /test/console.log(new RegExp('test').test(str)); //true
var str = 'This is a test';console.log(new RegExp('tes')); // /tes/console.log(new RegExp('tes').test(str)); //true
var str = 'This is a test';console.log(new RegExp('Test')); // /Test/console.log(new RegExp('Test').test(str)); //false
参数2:为属性(修饰符)
//忽略大小写var str = 'This is a test',reg = new RegExp('Test', 'i');console.log(reg); // /Test/iconsole.log(reg.test(str)); //true
//修饰符只有i的时候//两个test时,只匹配一个var str = 'This is a test. Test is important.',reg = new RegExp('Test', 'i');console.log(reg); // /Test/iconsole.log(str.match(reg));/*** console.log(str.match(reg)):* ["test", index: 10, input: "This is a test. Test is important.", groups: undefined]* 0: "test"* groups: undefined* index: 10* input: "This is a test. Test is important."* length: 1* __proto__: Array(0)*/
//修饰符有gi的时候//两个test时,全部匹配var str = 'This is a test. Test is important.',reg = new RegExp('Test', 'gi');console.log(reg); // /Test/iconsole.log(str.match(reg));/*** console.log(str.match(reg)):* ["test", "Test"]* 0: "test"* 1: "Test"* length: 2* __proto__: Array(0)*/
要求
- 字符串片段,大小写默认敏感
- 连续的
修饰符
i:igore case 忽略大小写g:global 全局匹配m:multi-line 多行匹配
//i忽略大小写var reg = /Test/i,str = 'This is a test';console.log(reg.test(str)); //truevar reg2 = /Test/,str = 'This is a test';console.log(reg2.test(str)); //false
//全局匹配//没加g匹配一个到一个结果var reg = /Test/i,str = 'This is a test. Test is important.';//match()匹配所有正则里面的内容归纳成一个数组console.log(str.match(reg));/*** console.log(str.match(reg)):* ["test", index: 10, input: "This is a test. Test is important.", groups: undefined]* 0: "test"* groups: undefined* index: 10* input: "This is a test. Test is important."* length: 1* __proto__: Array(0)*///加了g匹配到两个结果var reg2 = /Test/gi,str = 'This is a test. Test is important.';console.log(str.match(reg2));/*** ["test", "Test"]* 0: "test"* 1: "Test"* length: 2* __proto__: Array(0)*/
// 多行匹配// 希望匹配一行开头的单词var reg = /Test/gim,str = 'This is a test. \nTest is important.';console.log(str.match(reg));/*** ["test", "Test"]* 0: "test"* 1: "Test"* length: 2* __proto__: Array(0)*/
创建
创建方法一:字面量
var reg = //;
此方法不适用套用变量
var v = 'Test';var reg = /v/gi; //null
创建方法二:实例化构造函数
var reg = new RegExp();
此方法适用套用变量
var v = 'Test';var reg = new RegExp(v, 'i');
字面量创建的与实例化的正则不是同一个正则:
var reg = /test/;var newReg = new RegExp('test');console.log(reg);console.log(newReg);reg.a = 1;console.log(newReg.a); //undefined
此时为什么会等于1?
同一个对象的引用
var reg = /test/;var newReg = RegExp(reg);console.log(reg);console.log(newReg);reg.a = 1;console.log(newReg.a); //1
又创建新的对象不同的引用
var reg = /test/;var newReg = new RegExp(reg);console.log(reg);console.log(newReg);reg.a = 1;console.log(newReg.a); //undefined
表达式
[]:区间^:非|:或
正则匹配时是正向往后匹配,并不会逆向重新匹配
匹配过的字符就不会去匹配
// 表达式 []//希望匹配第一位里符合1234567890的任意一位//希望匹配第二位里符合1234567890的任意一位//希望匹配第三位里符合1234567890的任意一位var str = '09wefjh0e0r9gj-0werfj',str1 = '098efjh0e0r9gj-0werfj',str2 = '098efjh0e0r912gj-0werfj';reg = /[1234567890][1234567890][1234567890]/g;console.log(str.match(reg)); //nullconsole.log(str1.match(reg)); //["098"]console.log(str2.match(reg)); //["098", "912"]
// 表达式 []//这里[]里是选择字符中间的一个,而不是一个范围//分析://[wx]选择中间的一个 x//[xy]选择中间的一个 y//[z]选择中间的一个 z//过程://1.w xy z匹配不了//2.x y zvar reg = /[wx][xy][z]/g,str = 'wxyz';console.log(str.match(reg)); //["xyz"]
//写区间var str = 'fiksodf090dg0erGEOGIV-G345;Lgf-',str1 = 'fiksodf090Dg0erGEOGIV-G345;Lgf-';//希望匹配第一位是数字//希望匹配第二位是大写//希望匹配第三位是小写reg = /[0-9][A-Z][a-z]/g;console.log(str.match(reg)); //nullconsole.log(str1.match(reg)); //["0Dg"]
// 表达式内部写 ^ 代表 非var str = 'fiksodf090dg0erGEOGIV-G345;Lgf-',str1 = 'fiksodf090Dg0erGEOGIV-G345;Lgf-';//希望匹配第一位不是数字//希望匹配第二位是大写//希望匹配第三位是小写reg = /[^0-9][A-Z][a-z]/g;console.log(str.match(reg)); //[";Lg"]console.log(str1.match(reg)); //[";Lg"]
// 表达式内部写 | 代表 或var str = '234siafdgosafdigj123sdfhsoarg',reg = /123|234/g,reg2 = /123|234[s-z]/g,// 希望或了之后再匹配reg3 = /(123|234)[s-z]/g;console.log(str.match(reg)); //["234", "123"]console.log(str.match(reg2)); //["234s", "123"]console.log(str.match(reg3)); //["234s", "123s"]
元字符
即正则使用的转义符号
\w:[0-9A-z_]\W:[^\w]非\w区间的所有字符\d:[0-9]digit 数字\D:[^\d]非数字\s:[\r\n\t\v\f]->[回车\换行\制表\垂直\换页]\S:[^\s]\b: 单词边界\B: 非单词边界.: 可以匹配除了回车和换行的所有字符
// \W \wvar reg = /\wab/g,reg1 = /\Wab/g,str = '234abc-%&',str1 = '234%abc-%&';console.log(str.match(reg)); //["4ab"]console.log(str.match(reg1)); //nullconsole.log(str1.match(reg1)); //["%ab"]
// \W \wvar reg = /[\w][\W][\w]/g,reg1 = /[\W][\W][\W]/g,str = '234%abc-%&';console.log(str.match(reg)); //["4%a"]console.log(str.match(reg1)); //["-%&"]
// \d \Dvar reg = /\dab/g,reg1 = /\Dab/g,str = '234%abc-%&';console.log(str.match(reg)); //nullconsole.log(str.match(reg1)); //["%ab"]
//\s \Svar reg = /\sab/g,str = '23 ab-$%';console.log(str.match(reg)); //[" ab"]
//\b \Bvar reg = /\bThis\b/g,str = 'This is a test';console.log(str.match(reg)); //["This"]
//.var reg = /./g,str = 'This\ris\na\ttest';console.log(str.match(reg));//["T", "h", "i", "s", "i", "s", "a", "\t", "t", "e", "s", "t"]
//如何匹配全部字符//希望匹配3位全部字符var reg = /[\w\W][\s\S][\d\D]/g,str = 'abcdefg';console.log(str.match(reg));//["abc", "def"]
原则
正则的两个原则:
- 不回头
- 贪婪模式(能匹配多就不匹配少)
量词
n+:{1, 正无穷}出现1次到正无穷n*:{0, 正无穷}出现0次到正无穷n?:{0, 1}出现0次到1次
//+//只要符合条件 有+的情况下,出现多少次,匹配出多少次结果//分析:匹配0-9A-Z包括_中任意一个字符可以出现1次或者多次的字符串匹配出来var reg = /\w+/g,str = 'abcdefg';console.log(str.match(reg)); //["abcdefg"]
//*//只要符合条件 有*的情况下,出现多少次,匹配出多少次结果//分析://1.匹配0 - 9A - Z包括_中任意一个字符可以出现0次或者多次的字符串匹配出来//2.匹配到结尾g 贪婪 0次: 发现仍有空, 就把空字符也匹配进去var reg = /\w*/g,str = 'abcdefg';console.log(str.match(reg)); //["abcdefg", ""]
//*//分析://匹配a非数字匹配不上 贪婪 遇到空//匹配b非数字匹配不上 贪婪 遇到空//...//原理://字符串从左到右,依次先匹配多,再匹配少,如果一旦匹配上就不回头匹配//贪婪匹配原则: 能匹配上多个,绝不匹配少个.var reg = /\d*/g,str = 'abcdefg';console.log(str.match(reg));//["", "", "", "", "", "", "", ""]
//?//一旦匹配上就不回头匹配 再匹配匹配一个空var reg = /\w?/g,str = 'abcdefg';console.log(str.match(reg));//["a", "b", "c", "d", "e", "f", "g", ""]
//n{x, y} 区间问题//注意这里有空格返回nullvar reg = /\w{1, 2}/g,str = 'abcdefg';console.log(str.match(reg)); //nullvar reg1 = /\w{1,2}/g,str = 'abcdefg';console.log(str.match(reg1)); //["ab", "cd", "ef", "g"]
//n{x,正无穷} 区间问题//{1,正无穷} === n+var reg = /\w{1,}/g,str = 'abcdefg';console.log(str.match(reg)); //["abcdefg"]//{0,正无穷} === n*var reg1 = /\w{0,}/g,str = 'abcdefg';console.log(str.match(reg1)); //["abcdefg", ""]//{0,1} === n?
//{5,正无穷}var reg = /\w{5,}/g,str = 'abcdefg';console.log(str.match(reg)); //["abcdefg"]//不足5位var reg = /\w{5,}/g,str1 = 'abcd';console.log(str1.match(reg)); //null
//量词的上尖角号 ^n//匹配任何以n开头的字符串var reg = /^ab/g,str = 'abcdabcd';console.log(str.match(reg)); //["ab"]//多行匹配也生效var reg1 = /^ab/gm,str1 = 'abcdabcd\nabcdabcd';console.log(str1.match(reg1)); //["ab", "ab"]
//问题:检查字符串是否以abcd开头和以abcd结尾//分析:以abcd开头任意字符匹配多次以abcd结尾var reg = /^abcd[\s\S]*abcd$/g,str = 'abcd123123abcd';console.log(str.match(reg)); //["abcd123123abcd"]
//问题:检查字符串是否以abcd开头和以abcd结尾, 并且开头结尾之间是数字var reg = /^abcd[\d]+abcd$/g,str = 'abcd123123abcd';console.log(str.match(reg)); //["abcd123123abcd"]
//匹配以138开头的11位手机号码var reg = /138[\d]{8}/g,str = '13812345678';console.log(str.match(reg)); //["abcd123123abcd"]
//?=n 匹配任何其后紧接着指定字符串n的字符串var reg = /a(?=b)/g,str = 'abcdabcd';console.log(str.match(reg)); //["a", "a"]
//?!n 匹配任何其后紧接着指定字符串n的字符串var reg = /a(?!b)/g,str = 'abcdaccda';console.log(str.match(reg)); //["a", "a"]
子表达式/反向引用
//xxxx// 子表达式 反向引用 方法// 子表达式方式: ()匹配谁 有表达式 有记忆匹配的是谁// \1 匹配到第一个子表达式 反向引用子表达式// /(第一个子表达式)\1(第二个子表达式)/g// 举例:// (a)\1 反向引用第一个子表达式// (a)\2 反向引用第二个子表达式// (a)\3 反向引用第三个子表达式var str = 'bbaaaaccaaaaiddddbaaaa',// \1 这里反向引用3次reg = /(a)\1\1\1/g,//分析:(\w)带记忆后面的都反向引用3次reg1 = /(\w)\1\1\1/g;console.log(str.match(reg));//["aaaa", "aaaa", "aaaa"]console.log(str.match(reg1));//["aaaa", "aaaa", "dddd", "aaaa"]
//xxyyvar str = 'aabbccddddddccceevv',//这里反向引用第一个子表达式1次,反向引用第二个子表达1次reg = /(\w)\1(\w)\2/g;console.log(str.match(reg));//["aabb", "ccdd", "dddd", "ccee"];
对象属性
reg.global:判断是否用greg.ignoreCase:判断是否用忽略大小写ireg.multiline:判断是否用换行mreg.resource:正则本体,如/(\w)/reg.lastIndex:查到跟exec()执行后类数组里面的index一样的值,可更改值,还能调整下标
问题:如果用reg.lastIndex更改index的值 并不是需要的,它会变得怎么样?
执行1次找到跟修改数字接近得匹配的下标,执行2次会被修改为下一轮的下标
对象方法
reg.test(str):判断是否能匹配出来reg.exec():执行
//exec()var reg = /123/g,str = '123123123123123';console.log(reg.exec(str));/*** 问题: 此类数组继承的是Array.prototype, 不是应该继承Object.prototype吗?* 自己写是继承Object.prototype, 但是这里的类数组是继承Array.prototype* console.log(reg.exec(str));* 打印出一个类数组* ["123", index: 0, input: "123123123123123", groups: undefined]* 0: "123"* groups: undefined* index: 0 //这里是下标/光标 第一次匹配exec()为0 第二次为3 第三次为6,第四次为9...直到匹配结束了就变回0 ,一轮一轮的匹配* input: "123123123123123"* length: 1* __proto__: Array(0)*/
在正则规则里包含反向引用子表达式时,执行exec()会在类数组中显示子表达式
var reg = /(\w)\1(\w)\2/g,str = 'aabbccddddddccceevvv';console.log(reg.exec(str));/*** ["aabb", "a", "b", index: 0, input: "aabbccddddddccceevvv", groups: undefined]* 0: "aabb"* 1: "a" 第一项子表达式是a* 2: "b" 第二项子表达式是b* groups: undefined* index: 0* input: "aabbccddddddccceevvv"* length: 3* __proto__: Array(0)*/
关于exec():
//不加g 返回单个值得数组// 加g 返回所有值的数组,具有记忆功能var str = 'cat,bat,sat,fat',reg = /.at/g,match1 = reg.exec(str),match2 = reg.exec(str),match3 = reg.exec(str),match4 = reg.exec(str);console.log(match1);//["cat", index: 0, input: "cat,bat,sat,fat", groups: undefined]console.log(match2);//["bat", index: 4, input: "cat,bat,sat,fat", groups: undefined]console.log(match3);//["sat", index: 8, input: "cat,bat,sat,fat", groups: undefined]console.log(match4);//["fat", index: 12, input: "cat,bat,sat,fat", groups: undefined]
正向预查
匹配一个字符串,但有条件,字符串后面指定一个特定的字符或者字符串
//找后面紧跟2的1var str = '1231231231',reg = /1(?=2)/g;console.log(str.match(reg)); //["1", "1", "1"]
贪婪模式
能匹配多,不匹配少
注:
?可以将贪婪模式改为非贪婪模式
//匹配大括号包含里面的内容var str = 'abcd{{efg}}abcd{{xyz}}',// 贪婪情况(默认)reg = /{{.*}}/g,//非贪婪情况//任意字符零次或多次//? 可以将贪婪模式改为非贪婪模式reg1 = /{{(.*?)}}/g;console.log(str.match(reg)); //["{{efg}}abcd{{xyz}}"]console.log(str.match(reg1)); //["{{efg}}", "{{xyz}}"]
能匹配多,不匹配少(贪婪)
//匹配0次或1次,先匹配1次然后匹配0次var str = 'aaaaaa',reg = /\w?/g;console.log(str.match(reg));//["a", "a", "a", "a", "a", "a", ""]
能匹配少就不匹配多(非贪婪)
//能匹配少就不匹配多,并把多次省略掉var str = 'bbbbbbbb',reg = /\w??/g;console.log(str.match(reg));//["", "", "", "", "", "", "", "", ""]
replace()
var str = 'JSplusplus',// str.replace('要被替换的字符串', '+');reg = /plus/;reg1 = /plus/g;console.log(str.replace('plus','+')); //JS+plusconsole.log(str.replace(reg,'+')); //JS+plusconsole.log(str.replace(reg1,'+')); //JS++
//xxyy -> yyxxvar str ='aabbccdd',reg = /(\w)\1(\w)\2/g;// 写法一//希望变成bbaaddcc// $可以拿到// 将第二个反向引用子表达式重复两遍,再重复两遍第一个反向引用子表达式两遍console.log(str.match(reg)); //["aabb", "ccdd"]console.log(str.replace(reg, '$2$2$1$1')); //bbaaddcc// 写法二console.log(str.replace(reg, function ($, $1, $2) {// console.log($, $1, $2);//第一次打印:当前被匹配字符串,第一个子表达式,第二个子表达式//aabb a b//第二次打印:当前被匹配字符串,第一个子表达式,第二个子表达式//ccdd c d// return $2$2$1$1; //报错return $2 + $2 + $1 + $1; //bbaaddcc}));
//js-plus-plus -> jsPlusPlusvar str = "js-plus-plus",reg = /-(\w)/g;console.log(str.replace(reg, function ($, $1) {console.log($, $1); //-p preturn $1.toUpperCase();})); //jsPlusPlus
//jsPlusPlus -> js_plus_plusvar str = "jsPlusPlus",//匹配大写reg = /([A-Z])/g;console.log(str.replace(reg, function ($, $1) {console.log($, $1); //P Preturn "_" + $1.toLowerCase();})); //js_plus_plus
//xxyyzz -> XxYyZzvar str = "xxyyzz",reg = /(\w)\1(\w)\2(\w)\3/g;console.log(str.replace(reg, function ($, $1, $2, $3) {// console.log($, $1); //xxyyzz xreturn ($1.toUpperCase() + $1 +$2.toUpperCase() + $2 +$3.toUpperCase() + $3);})); //XxYyZz
//aabbcc -> a$b$c$ -> 不能使用functionvar str = "aabbcc",reg = /(\w)\1(\w)\2(\w)\3/g;console.log(// str.replace(reg,'$1$$2$$3$') //a$2$3$str.replace(reg,'$1$$$2$$$3$$') //a$b$c$);
//aabbcc -> abc// 字符串去重var str = "aaaaabbbccccccccc",// 匹配0次或多次reg = /(\w)\1*/g;console.log(str.replace(reg, "$1"));
//100000000000 -> 100,000,000,000var str = "100000000000",// 匹配0次或多次// \B 非单词边界reg = /(\d{3})/g;reg1 = /(\d{3}\B)/g;console.log(str.replace(reg, "$1,")); //100,000,000,000,console.log(str.replace(reg1, "$1,")); //100,000,000,000
