正则表达式
正则表达式,又称规则表达式,就是定义一个规则来操作字符串,比如查询,字符串中有没有哪个部分和规则相匹配的,再比如替换,查到之后,对匹配的部分字符串进行替换。正则表达式只对字符串起作用,只能操作字符串。 许多语言都支持正则表达式,JS 是通过内置RegExp对象来支持,所以创建正则(规则)表达式的方式有两种,一种是对象字面量,一种是构造函数。
// 对象字面量 let regLiteral = /\d/g; // 构造函数 let regCons = new RegExp('\\d', "g");
通常使用对象字面量,只有在事先不确定的情况下需要动态生成时,才使用构造函数。\d表示数字,g是标识符(global),表示全局匹配, 因为正则表达式匹配字符串时,默认是惰性匹配,它只要成功匹配到一个字符,就不会再进行匹配,举个列子,找到字符串中的数字,转化成字符X.
const regNoG = /\d/; const regWithG = /\d/g; const str = 'a4c5d6' console.log(str.replace(regNoG,"X")) //没有g标志,成功匹配到第一个数字4就停止 aXc5d6 console.log(str.replace(regWithG,"X")) //有g,从字符串开始到字符串结束,全部匹配,aXcXdX
\d 就是一种规则,定义规则的时候有两种基本字符,一种是原义文本字符,字符就是看到的字面意思,比如/a/只会匹配小写字母a,a就是a,没有其他任何意思。一种是元字符,有着特殊含义,比如\d,它并不是字面上看上去的\和小写字母d的组合,而是表示任意的数字。 正则表达式中有以下元字符:. * ? ^ 等, 也就是说,这些字符都有特殊的意思.
默认情况下,正则表达式匹配字符时,都是一个字符一个字符的去匹配,正则表达式中的一个字符对应字符串的一个字符。\d就是一个字符,它先匹配'a',再匹配'4',再匹配'c' 等。但有时候,我们想让正则表达式中的多个字符去匹配一个字符,多个字符去匹配一个字符时,只要这个字符是多个字符中的一个,就算匹配成功。比如,字符是abc中的一个就行,用[abc], 中括号作为一个整体,去匹配字符串中一个字符,这一个字符只要是abc中的一个就算匹配成功。
const regNoG = /[abc]/; const str = 'a4c5d6' console.log(str.replace(regNoG,"X")) //X4c5d6
如果只要是26个字母一个,就要在[]列出26个字母,有点麻烦。正则表达式提供了范围的书写,-在[]中表示字符范围,0-9,a-z ,并且还可以连写。
const regNumber = /[0-9]/; const regWord = /a-zA-Z/ const regWordNumber = /[0-9a-zA-z]/
在[]中其实还可以用一个字符,那就是^, 它表示取反,匹配除括号中的字符之外的任意一个字符。[[^xy] 匹配除xy 之外的任一字符,[^0-9] 就表示除了0-9之处的任意字符。[^0-9a-zA-z] 则表示除了0-9,a-z A-Z 之外的任意字符。对于这些常用的表示符,正则表达式也提供了一些预定义类,[0-9]表示数字,可以用\d(digital)来代替,[^0-9] 表示非数字,用\D来代替,小写是什么意思,大写就是它的取反。还有\s, \w, \W等。\s(space)表示空白符, \S就表示非空白符. \w(word), 表示单个字符(字母,数字加下划线), \W 表示除字母,数字和下划线以外的字符,这里还有一个 点号 . 表示除回车和换行符之外的任意字符。/ab\d./ 表示ab后面紧跟数字,后面是任意字符
let reg = /ab\d./g;
let string = 'there is ab5td and abds '
let replaceString = string.replace(reg,"A");
console.log(replaceString) // there is Ad and abds
这个正则表达式匹配的是ab5t,整个正则表达式定义了4个字符(a, b \d, .),只能匹配4个字符,那要匹配aaabbb,每个字符都要写好多遍,可以用量词。量词就是表示数量,一个字符出现了多少次。它有以下几种
+:表示出现一次或多次(至少出现1次),+号吗,越加越多,越来越多,所以是多次。/ab+/, 字符串‘ab’, ‘abb’, ‘abbbbb......’ 都可以和它匹配。
? : 表示出现 0次或1次(最多出现1次), ? 表示是不是,是就是1, 不是就是是0, 所以表示0 或1. /ab?/ , 字符串‘a’, ‘ab’ 都可以和它匹配。
* : 表示出现0,1 或多次, *都是通配符的意思, 通通匹配, 所以出现0次,1次或任意次都可以. /ab*/ , 字符串‘a’, ‘ab’ ,'abb', ‘abbbbb......’ 都可以和它匹配。
{n}:表示出现n次,如/\d{5}/ 表示一个数字要出现5次,如 字符串‘12345’ 可以和它匹配
{n,m}: 至少出现n次,最多出现m次 /\d{3,5}/ , 字符串‘123’, ‘1234’, ‘12345’ 都可以和它匹配。
{n, }: 至少出现 n次; /\d{2,}/ 字符串‘12’, ‘123’, ‘12345’ , "12333....." 都可以和它匹配。
如是在正则表达式中,出现量词,如/\d{3,5}/, ‘123’, ‘1234’, ‘12345’ 都可以和它匹配,而我们的字符串是'123456', 那它怎么匹配,是区配字符串中 的 ‘123’, ‘1234’, 还是‘12345’ ? 这时正则表达式匹配最多出现次数,就是里面的12345, 这也称之为贪婪模式。
let reg = /\d{3,5}/g;
let string = '123456'
let replaceString = string.replace(reg,"X");
console.log(replaceString) //X6, 可见它匹配的是12345,最多出现次数
那怎么样才能让它匹配最小出现次数呢? 就是匹配出现3次,那也好办,就是在量词后面加? 正则表达式改为 /\d{3,5}?/g, 这也称之为非贪婪模式或懒惰模式
let reg = /\d{3,5}?/g;
let string = '123456'
let replaceString = string.replace(reg,"X");
console.log(replaceString) //XX,123=> X, 456=> X,匹配最少次数
对于非贪婪模式或贪婪模式,这里还要注意一点,先看一下下面的例子
let reg = /\d{3,5}/g;
let string = '12345678'
let replaceString = string.replace(reg,"X");
console.log(replaceString) //XX
上面我们执行的是贪婪模式,它会匹配最多出现的次数,也就是5次,那么结果应该是X678才对,为什么是XX呢? 当贪婪模式匹配字符串的时候,当已经不够最大次数的匹配的时候,会选择更小次数的匹配。比如这里首先匹配12345, 剩下678, 它已经不够最大次数匹配,那就执行更小的次数匹配,那就是3或4,678 正好是3, 所以也就变成了X
2, 标识符: 上面的g 就是一个标识符,除了它,还有两个i, m
i,是ignore, 忽略大小写。这很好理解, js就是区分大小写的,如果用一个大写字母去区配字符串的话,它只会去找字符串中的大写字母。如果加上i修饰符,就表示可以忽略大小写,小写字母也是ok的。
// 没有加i标识符,可以看到它只会匹配字符串中的小写a let reg = /a/g; let str = 'aAbbcc' let strReplaced = str.replace(reg,"G") console.log(strReplaced) // GAbbcc
// 加i标识符,可以看到大写A小写a都会匹配 let reg = /a/gi; let str = 'aAbbcc' let strReplaced = str.replace(reg,"G") console.log(strReplaced) // GGbbcc
5, 边界类
边界类主要用四个: ^ 表示以什么开始,$表示以什么结尾, \b, boudary 表示单词边界,\B 表示非单词边界。边界就是单词和单词相隔的地方,最明显的就是空格。
写一个例子来看一下可以更为直观。
let reg = /is/g; let string = 'this is a dog ' let replaceString = string.replace(reg,"IS"); console.log(replaceString) // thA IS a dog
两个is 都被替换掉了,这符合预期。但我们怎么只替换中间的is,中间的is 有一个特点,它是一个单词,因为前面和后面都是空格,使得它与其它单词分开了,正因为有空格的存在is 才成为了一个单词,所以空格是单词边界, \b 可以匹配到它。把正则表达式改成 /\bis\b/g, 可以看到输出 this IS a dog, 只匹配第二个。 如果只想改变第一个呢?那好办,因为第一个is被包含在一个单词中,所以它的前面不是单词边界,直接改成\B 就可以了, 正则表达式改成 /\Bis\b/g 就可以了.
^ 和$ 很好理解, /^T/以大写字母T开头即可, /T$/ 以大写字母T结束即可, 要注意它们的书写位置,一个在前,一个在后. 如果字符串中有换行符, let string = '@123\n@456\n@789', 字符串表现地就像有三行一样. 以下是chrome 浏览器中的输出
这时如果以/^@/g 去进行匹配的话,它只会匹配第一个@,
let reg = /^@/g; let string = '@123\n@456\n@789' let replaceString = string.replace(reg,"X"); console.log(replaceString) /* * X123 * @456 * @789 */
如果想匹配三个,那就要用到m修饰符,多行标示符,它表示如果字符串中有换行符,那就把字符串当成多行看待。m 标示符,只有在正则表达式中有^或&时才起作用,这也是把m标示符放到这里说明的原因。
7 分组
上面我们说过,正则表达式是一个字符一个字符的去匹配,所以如果要匹配一个单词如is ,就是必须写/is/, 如果想要匹配is 出现3次,就只能写/isisis/, 我们想使用量词/is{3}/,但这只表示s出现3次,那怎么修改才能表示is 出现三次,把is 用括号括起来,/(is){3}/. 用括号把is括起来,就表示是它是一个整体,一个组,它们要一起出现,才算匹配成功,所以称之为分组。分组有一个重要的概念,就是引用,当正则表达式中有分组时,我们可以获取到分组的内容,怎么获取,就是$n, n表示数字,$1 表示第一个分组的内容,$2 表示第二个分组的内容,$3 表示第三个分组的内容,依次类推,其实这里还有一个$0, 它比较特殊,所以单列出来,它表示,整个正则表达式匹配成功的内容。 可能不太好理解,写一个例子
写一个时间字符串 '2017-03-05', 正则表达式 /\d{4}-\d{2}-\d{2}/g 可以匹配它,如下,
let reg = /\d{4}-\d{2}-\d{2}/g; let string = '2017-03-20' let replaceString = string.replace(reg,"X"); console.log(replaceString) //X
这时我们在正则表达式中加上分组,可以看到没有产生影响
let reg = /(\d{4})-(\d{2})-(\d{2})/g; // 加上分组,就是把三段分别用括号括起来 let string = '2017-03-20' let replaceString = string.replace(reg,"X"); console.log(replaceString) //X
但是有了分组之后,就有引用,我们通过$n可以获取到分组内容,这里有三个分组,可以用$1, $2, $3 分别获取到三个分组内容, 把“X” 分别换成立$1,$2,$3看一个
把replace 中的 “X” 换成$1, 可以看到输出2017, 表示$1获取的就是第一个分组 (\d{4}) 的内容
let reg = /(\d{4})-(\d{2})-(\d{2})/g; // 加上分组 let string = '2017-03-20' let replaceString = string.replace(reg,"$1"); console.log(replaceString) //2017
把replace 中的 “X” 换成$2, 输出03, 表示$2获取的就是第二个分组 (\d{2}) 的内容, $3 可以自己试一下。
let reg = /(\d{4})-(\d{2})-(\d{2})/g; let string = '2017-03-20' let replaceString = string.replace(reg,"$2"); // $2 console.log(replaceString) //03
$1, $2,$3都是字符串,可以进行任意组合,我们就可以得到想要的结果, '$2/$3/$1', 我们就可以把一种日期格式,转化成另外一种日期格式
let reg = /(\d{4})-(\d{2})-(\d{2})/g; let string = '2017-03-20' let replaceString = string.replace(reg,"$2/$3/$1"); console.log(replaceString) // 03/20/2017
忽略分组:$n获取分组的内容和正则表达式中的分组是一一对应的,正则表达式中有几个分组,它从左向右就会对应几个$n, $1 永远获取的是第一个分组内容,$2 永远获取的都是第二个分组的内容,但有时候,我们想跳过某个分组,获取它下一个分组,比如$2 获取第三个分组的内容,这时正则表达式中的第二个分组前面要加 ?:, 表示获取内容的时候可以忽略这个分组
let reg = /(\d{4})-(?:\d{2})-(\d{2})/g; let string = '2017-03-20' let replaceString = string.replace(reg,'$2'); // $2 console.log(replaceString) //20
在上面的代码中,我们在第二个分组前面加了 ?:进行了忽略了,所以$2 获取的是第三个分组的内容。
分组还有一个概念,就是重复子项,它用\1,\2来表示,它们只用在正则表达式中, \1 代表的也是第一个分组,\2代表的是第二个分组,它们主要的作用是匹配以下这种字符串,
、121, 222, 323, 424, 第一个数字和第三个数字相等,但又是不确定的数字, 所以在书写正则表达式时,必须保证第一项和第三项相等,第三项是第一项的复制,\1, 就是正则表达式中第一个分组的复制。
let reg = /(\d)2\1/g; let string = '121 222 323 424' let replaceString = string.replace(reg,'X'); console.log(replaceString) // X X X X
还有一个或的概念,符号表示为|, /ab|ac/, 当它们去匹配字符串时,只要出现其中的一个就算成功