JavaScript 正则表达式

本文详细介绍了JavaScript中正则表达式的模式构建,包括字符类、组和范围、量词、断言以及Unicode属性转义等核心概念,并探讨了正则表达式在字符串匹配中的应用,如test、exec等方法的使用。同时,提到了String对象的相关方法如replace、match、matchAll、search和split在处理正则表达式时的角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正则表达式的模式;该模式有哪些字符组成,有普通字符,有特殊字符,可以其中一种,也可以是它们的组合;需要通过了解正则表达式的断言、字符类、组和范围、量词、Unicode属性转义等知识点,编写正则表达式的模式;利用表达式对象的方法test和exec,以及String的replace、match、matchAll、search、split使用正则表达式的模式匹配字符串中字符组合;若字符串中有 子字符串 能 完整地匹配正则模式,则表示匹配成功;相当于在一个大的字符串找一个符合指定要求的的字符串,这个指定要求就是正则表达式;

1、组和范围:

a, []用中括号括起的字符集合如[a-c], 表示匹配括号中a到c任意一个字符,'-'表示范围,也有相反意思的如[^a-c]表示不能匹配到中括号中a到c的任意一个字符;

b,'-'用连接字符'-'表示范围如[a-z]中的'-',但是如果是[az-]中的'-'表示一个字符即连接字符会被考虑匹配即表示匹配'az-'中任意一个而不是表示范围;

c, 用'|'表示可选匹配其中之一;

d, \n(n是正数, 跟使用具名组类似,如\1表示使用第一被记录的匹配项,能被记录的匹配项一般是形如(x),但断言虽有圆括号但匹配项不会被记录,且其实不会被匹配),

e, (?<name>x)(具名捕获组,可以通过groups[name]得到匹配项,可以用\k<name>使用具名匹配项如/^(?<name1>abc)jk\k<name1>/ 则匹配字符串'abcjkabcnm'中'abcjkabc',且字符'abc'也是一个捕获组匹配项,作为match结果的一个元素)

f, \k<name>(表示使用该具名组匹配到的匹配字符),

h,(?:x)(非捕获组,不会被记录,所以不能使用\n(n是正数)拿到该匹配项),

i, (x)(表示捕获组,会被记录,比较消耗性能) 

//[],表示字符集,只要能匹配其中任意一个字符则匹配成功
//|,它是个特殊字符,表示或、两者之一,若想把它当普通字符,可以用转义字符'\'将其转义成普通字符或者放在字符集[]中
//-,在字符集中,若处于字符之间表示范围,介于两字符之间的所有字符(ASCII码),否则表示普通字符连接符'-';
//(),表示一个捕获组,会记录下该匹配项,能在exec和match等方法的结果中拿到该结果
//\n(n表示正整数如\1),表示第几个捕获组,能在模式使用第n个捕获组的匹配结果
//(?:),表示非捕获组,即与()相反
//(?<name>),具名捕获组,能在结果的groups出现
//\k<name>,在模式使用具名捕获组的匹配结果

//[] & '-'
console.log(    /[7-b]/.test('a'), 
                /[7-b]/.test('-'), 
                /[7-b-]/.test('-'),
                /[abc]/.test('b') );
// true   false  true  true
// //如果表示范围的顺序不是正序时,在创建正则表达式时会报错
// console.log( /[b-7]/.test('a'));
// //SyntaxError: Invalid regular expression: /[b-7]/: Range out of order in character class

// '|'
console.log(    /[u|y]/.test('|'), 
                /u|y/.test('|'), 
                /u|y/.test('u'), 
                /u|y/.test('y'));
//true   false   true  true

//() & '|' & (?:)
console.log(    /abc(km|ui)/.exec('abcui'), 
                /abc(?:km|ui)/.exec('abcui'), 
                /abc(?:km|ui)/.exec('abckm'));
// ["abcui", "ui", index: 0, input: "abcui", groups: undefined] 'ui'作为第一个捕获组,作为下标为1的值
// ["abcui", index: 0, input: "abcui", groups: undefined]  (?:)化()为非捕获组,所以没有该组
// ["abckm", index: 0, input: "abckm", groups: undefined]  '|'表示两者之一,也可以用两个'|'表示三者之一
//第一个元素表示模式匹配到的结果

//() & \n & (?:) 
//为了反应后两句的区别,于是加上边界符'$', '$'表示字符串末尾
console.log(    /abc(km|ui)\1\2(?:no)/.exec('abcuiuino'), 
                /abc(km|ui)\1\2(?:no)/.exec('abcuiui\2no'), 
                /abc(km|ui)\1\2(no)$/.exec('abcuiuinono'),
                /abc(km|ui)\1\2(no)$/.exec('abcuiuino'))
// null 
// ["abcuiuino", "ui", index: 0, input: "abcuiuino", groups: undefined] 
// null 
// ["abcuiuino", "ui", "no", index: 0, input: "abcuiuino", groups: undefined]
//第一句的结果是null,是因为\2超出了捕获组的数量,于是被当作普通字符串'\2',
//但其实是把2进行转义,而且还转义成功了;
//于是第二句是true
//第三句\2使用的第二个捕获组在模式的后面,于是它不能提前拿到后面的捕获组,
//于是只能拿到类似于占位符的模式(?:),即空字符串
//于是第四句的结果是true

//(?<name>) & \k<name>
console.log(/abc(km|ui)(?<test>nk)HH\k<test>/.exec('abcuinkHHnkMM'));
//["abcuinkHHnk", "ui", "nk", index: 0, input: "abcuinkHHnkMM", groups: {test: "nk"}]
//在groups记录下了具名捕获,同时也记录下一个普通捕获

// console.log(/abc(km|ui)(?<test>nk)HH\k<test>\k<hello>/.exec('abcuinkHHnkMM')); 
// //因为引用了不存在的\k<hello>
// //Uncaught SyntaxError: Invalid regular expression: /abc(km|ui)(?<test>nk)HH\k<test>\k<hello>/: Invalid named capture referenced

console.log(    /abc(km|ui)HH\k<hello>/.exec('abcuiHH'),
                /abc(km|ui)HH\k<hello>/.exec('abcuiHH\k<hello>'));
// null
// ["abcuiHHk<hello>", "ui", index: 0, input: "abcuiHHk<hello>", groups: undefined]
// '\'对k的转义还是k,但是虽然转义没有失败,但平时可不能这样使用;

2,断言:

a,边界断言:^(表示字符串开头), $(表示字符串末尾), \b(表示单词边界), \B(表示非单词边界);

其他断言(不会被记录,即不会出现在match结果的元素中):

b,向前断言x(?=y):表示字符x被y跟随时匹配x,如(/a(?=b)/).test('abc') === true, (/a(?c)/).test('abc') === false, 另外,断言条件出现的字符不计入匹配结果,也就是第一个匹配到的结果是字符串'a',还有就是若要继续向匹配,需要考虑条件中的字符,即(/a(?=b)bc/).test('abc') === true才能匹配到后面字符; 

c,向前否定断言x(?!y):表示字符x不被y跟随时匹配x; 

d,向后断言(?<=y)x: 表示字符x跟随字符y时匹配x;

e,向后否定断言(?<!y)x :表示字符x不跟随字符y时匹配x;

//^用在模式的开头表示从字符串的第一个字符开始匹配;放在字符集的第一个位置表示反向字符集,
//字符集的其他位置表示普通字符'^';
//若要在其他地方使用需要使用转义字符'\'转义,\^;否则会出现意想不到的问题.
//^匹配输入的开始。如果多行标志被设置为 true,那么也匹配换行符后紧跟的位置。
//$用在模式的最后一个字符,表示匹配到字符串最后;
//放在字符集中,表示普通字符'$'; 若放在其他位置,需要使用\转义;否会有问题
//$ 匹配输入的结束。如果多行标志被设置为 true,那么也匹配换行符前的位置。
//\b, 表示匹配一个单词的边界,\B与\b相反
//x(?=y);向前断言,表示x被y跟随的条件成立的话,则匹配x成功;
//(?=y)只是一个条件,于是它不被记录,字符y也不会出现在匹配结果的字符串中
//x(?!y);向前否定断言,表示x不被y跟随的条件成立,则匹配x成功;同上;
//(?<=y)x;向后断言,表示x跟随y的条件成立,则匹配x成功;同上;
//(?<!y)x;向后否定断言,表示x不跟随y的条件成立,则匹配x成功;同上;
//顺带一提'?'的使用: ?可以出现在很多地方,是个特殊字符,
//1,如上述的使用,以及在捕获组中的使用;
//2, 用在某个字符后面,表示数量0个或1个;
//3,用在量词(包括?)后,取消其默认的贪婪匹配的特性;
//4,使用转义字符'\'将其转义为普通字符'?'


//^的使用, /[^]/表示匹配任意一个字符
console.log(    /^abc/.test('abc'),  
                /^abc/.test('babc'),  //字符串不是以'abc'开头
                /[^a]bc/.test('abc'),  //字符串没有非a的类似bbc的子字符串
                /[^a]bc/.test('bbc'),  
                /[a^]/.test('^'),
                /abc\^/.test('abc^'))
//true  false  false  true true true

//$
console.log(    /abc$/.test('mkabc'),
                /abc$/.test('abcc'), //字符串没有以'abc'结尾
                /^abc$/.test('mkabc'), //字符串没有以abc开头、没有完整匹配整个模式
                /^abc$/.test('abc'), 
                /[$abc]/.test('$'),
                /abc\$a/.test('mkabc$aLL'));
//true false false true true true true

//\b & \B
console.log(    'This is-a test!'.match(/\b[a-z-]+\b/ig),
                'This is-a test!'.match(/\B[a-z]+\B/ig),
                'This is-a test!'.match(/\b[a-z]+\B/ig));
// ["This", "is-a", "test"]  ["hi", "es"]   ["Thi", "i", "tes"]

//x(?=y)  &  x(?!y)   &  (?<=y)x   & (?<!y)x   &  ?的使用
console.log(    /abc(?=KL)/.exec('abcKL'),  //匹配结果的字符串是'abc','KL'只是条件
                /abc(?=KL)/.exec('abcCC'),  //条件不成立,匹配失败
                /abc(?!KL)/.exec('abcCC'),
                /(?<=KL)abc/.exec('KLabc'),
                /(?<!KL)abc/.exec('KLabc'), //条件不成立,匹配失败
                /a?b/.exec('b'), //匹配成功,匹配结果'b'
                /a?b/.exec('aab'),//匹配结果'ab' 
                /a\?b/.exec('a?b'),
                /a+/.exec('aaaaab'),
                /a+?/.exec('aaaaab')); 

// ["abc", index: 0, input: "abcKL", groups: undefined] 
// null 
// ["abc", index: 0, input: "abcCC", groups: undefined] 
// ["abc", index: 2, input: "KLabc", groups: undefined] 
// null 
//["b", index: 0, input: "b", groups: undefined] 
//["ab", index: 1, input: "aab", groups: undefined] 
//["a?b", index: 0, input: "a?b", groups: undefined]
//["aaaaa", index: 0, input: "aaaaab", groups: undefined]
//["a", index: 0, input: "aaaaab", groups: undefined]

//注意 模式/abc(?=KL)/ 相当于是'abc'后有'KL'条件下的 /abc/, 以此,那么 /abc(!=KL)ty/则表示 模式/abcty/
//所以会有以下的结果
console.log(    /abc(?=KL)ty/.test('abcKLty'), //匹配失败
                /abc(?=KL)KLty/.test('abcKLty'));
//false  true

3,字符类:

a,\d(表示一个阿拉伯数字[0-9]),,\D(一个非阿拉伯数字的字符[^0-9]),

b,\w(表示一个拉丁字母数字和下划线[a-zA-Z0-9_],表示单个字符),\W(跟\w相反[^A-Za-z0-9_]),

c,\s(匹配一个空白字符,空格、换行、tab等等\r,\t,\v,\n等,一个tab有四个空白符,所以/\s{4}/匹配一个tab,/\s/只是其中一个空白),\S(与\s相反),

d, dot '.'(一是表示匹配除行终止字符(\n,\r, \u2028,\u2029)之外的任意一个字符,二在字符集中,失去了它特殊性,就表示一个dot '.';三如果s(dotAll)被设置,那它也会匹配换行符;[^] 匹配任意字符),

e,\(特殊字符'\'表示转义,用在特殊字符前表示将特殊字符转为普通字符;用在普通字符前表示将其转为特殊字符;),

f, [\b](表示一个退格U+0008, 不同于\b);

还有其他的字符类( \0:匹配 NULL(U+0000)字符, 不要在这后面跟其它小数,因为 \0<digits> 是一个八进制转义序列,不同空格符;\uhhhh:表示UTF-16;\u{hhhh} or \u{hhhhh}表示Unicode;等),日后用到再补充!!

//\w \W
//\d \D
//\s表示一个空白符, \S相反
// . 表示处换行符外的任意一个字符
//\

console.log(    /\w/.exec('abc'), 
                /\W/.exec('abc'), 
                /\W/.exec('+-*\\'));
// ["a", index: 0, input: "abc", groups: undefined] 
// null 
// ["+", index: 0, input: "+-*\", groups: undefined]

console.log(    /\d/.exec('123'), 
                /\D/.exec('123'), 
                /\D/.exec('abc'));
// ["1", index: 0, input: "123", groups: undefined] 
// null 
// ["a", index: 0, input: "abc", groups: undefined]

console.log(    /\s/.exec(' tr'), 
                /\s/.exec('    ml'), 
                /\S/.exec('\n'),
                /\S/.exec('abc'));

// [" ", index: 0, input: " tr", groups: undefined] 
// [" ", index: 0, input: "    ml", groups: undefined] 
// null 
// ["a", index: 0, input: "abc", groups: undefined]

console.log(    /[abc.].\./.exec('...'), //字符集中'.'是个普通字符,
                                       //第二个'.'是特殊字符表一任意一个非换行符,
                                       //第三个被转义为普通字符,已没有作为特殊字符的含义
                /[abc.].\./.exec('.d.'), 
                /[abc.].\./.exec('.dl'));
// ["...", index: 0, input: "...", groups: undefined] 
// [".d.", index: 0, input: ".d.", groups: undefined] 
// null

console.log( /\^abc\./.exec('kl^abc...')); //^被转为普通字符'^'
// ["^abc.", index: 2, input: "kl^abc...", groups: undefined]

4,量词:

a, 贪婪匹配(匹配最大次数):x*(表可以示匹配0个或多个x),x+(表示至少匹配一个x,相当于{1, }),x?(表示匹配0个或1个x),x{n, }(表示匹配至少n个x), x{n, m}(表示n-m(包括)个x),x{n}(表示精确匹配n个x);

b, 非贪婪(即在贪婪匹配的后面再加上一个'?',匹配最小次数):x*?(匹配最小次数0次,所以(/g*?/).test('jkm') 也是true),x+?(匹配最小次数1,如(/g+?/).test('hkj') === false, (/g+?/).test('hgkl')===true)),x??(最小匹配0次),x{n, }?(表示最小匹配n次),x{n, m}?(表示最小匹配n次),x{n}?(表示匹配n次)

//以下是一些量词的表达形式 ----------
//*表示匹配前一个表达式0次或多次
//+表示匹配前一个表达式1次或多次
//{n}精确匹配n个表达式
//{n,}至少匹配n个表达式
//{n,m}匹配n个到m个表达式
//?表示匹配前一个表达式0次或1次;跟在量词后面,将量词的贪婪变为非贪婪即匹配尽量少的字符;
//可以用'\'将'*'等转为普通字符。

console.log(    /a*bc(?:KL)*ty/.exec('abcKLty'), 
                /a*bc*/.exec('aaaabcccc'),  //贪婪匹配
                /a*?bc*?/.exec('aaaabccc')); //非贪婪匹配, 但是前面的字符'a'还是
                                //'贪婪'匹配了,只能用匹配的顺序使然来解释,
                                //即模式中的'a'与字符串的第一个'a'匹配到,接下来中间有
                                //好几个'a'之后才到'b',这是模式能匹配到的,
                                //于是最后直接返回匹配成功的完整结果。
// ["abcKLty", index: 0, input: "abcKLty", groups: undefined] 
// ["aaaabcccc", index: 0, input: "aaaabcccc", groups: undefined] 
// ["aaaab", index: 0, input: "aaaabccc", groups: undefined]

console.log(    /abc+/.exec('abccc'), 
                /abc+?/.exec('abccc'));
// ["abccc", index: 0, input: "abccc", groups: undefined] 
// ["abc", index: 0, input: "abccc", groups: undefined]

console.log(    /abc{2}/.exec('abcccc'));

console.log(    /abccc{2,4}/.exec('abccccc'), 
                /abc{2,4}?/.exec('abccccc'),  //非贪婪匹配,匹配最小个数2个
                /abc{2,4}b/.exec('abcccccb')); //字符串中'c'超出了4个,
// ["abccccc", index: 0, input: "abccccc", groups: undefined] 
// ["abcc", index: 0, input: "abccccc", groups: undefined] 
// null
console.log(    /abccc{2,4}/.exec('abccccc'), 
                /abc{2,}?/.exec('abccccc'),  //非贪婪匹配,匹配最小个数2个
                /abc{2,}b/.exec('abcccccb')); //
// ["abccccc", index: 0, input: "abccccc", groups: undefined] 
// ["abcc", index: 0, input: "abccccc", groups: undefined] 
// ["abcccccb", index: 0, input: "abcccccb", groups: undefined]

5,Unicode属性转义 (貌似是ES2018新加的匹配规则,IE浏览器不支持):每个属性名可能有0个或多个简写的别名(如Emoji_Component的简写别名EComp),每个属性名对应着多个属性值,属性值也有相应的简写(如General_Category的属性值Letter的简写名L),属性值表示匹配范围,属性名也表示匹配范围,一个字符可以在不同属性名或属性值中被匹配(比如表情符号微笑😊在属性名'Extended_Pictographic' 和'Emoji' 以及'Emoji_Presentation'中可以匹配到);另外正则表达式的修饰符'u'(表示unicode)是必要的,如/\p{Emoji}/u;Unicode属性转义比较强大,可以匹配多种语言(如\p{Script=Latin}用来匹配拉丁文字);等等;知识点其实挺多的,之后有遇到再补充!

a,\p{unicode属性名=unicode属性值}或者\p{unicode属性值};\P{unicode属性值}(与\p相反),比如\p{General_Category=Letter}或\p{Letter}

b,\p{unicodeBinary属性名};\P{unicodeBinary属性名}(与\p相反,大写都表示不匹配),比如\p{Emoji}

6,一般如果模式不是动态的,可以直接用字面量表示如var regex = /^[a-z](abc | mop)/; 如果是动态,可以使用new运算符创建一个正则表达式如var test = '^[a-z](abc | mop)'; var regex1 = new RegExp(test); regex和regex1的模式是一样的;两者的不一样是前者是脚本加载后就编译,后者在执行到了编译;正则表达式的test(string)和exec(string)方法:test方法简单地返回boolean值,exec方法在有匹配时,都只是返回第一个完整匹配的信息;

new RegEpx(str | regEpx [, flagsStr]):  flagsStr可以是字符 'i'(忽略大小), 'm'(多行匹配,即不仅仅搜索第一行), 'g'(全局,当/^abc/g.exec('aaa\nabc') === null; 而/^abc/m.exec('aaa\nabc')!==null; 以及 /.abc/gs.exec('aaa\nabc') !== null; /.abc/ms.exec('aaa\nabc') !== null,可以看出flag 'g' 和'm'的不同), 'u'(匹配Unicode字符), 's'(表示dot '.'也匹配换行符), 'y'(粘性搜索)的任意组合;

RegExp实例属性dotAll(表示模式中dot '.' 也匹配换行符,启用flag s,dotAll的值为true),source,还有其他的属性;

//注意,模式中空格也是有效的表达式,如(/abc bc/).test('abcbc') === false; ----------
//(/abc bc/).test('abc bc') === true
var regex = /^[a-z](abc|mop)/;
//或者/abc/g
var test = '^[a-z](abc|mop)';
var regex1 = new RegExp(test);
//或者new RegExp('abc', 'g');
console.log(regex, regex1);
///^[a-z](abc|mop)/ /^[a-z](abc|mop)/
//使用正则对象的test和exec方法 -----------------
//test方法比较简单,若是字符串没有正则模式能够匹配到的子字符串,则返回false,若有则返回true
//test若返回true,那么exec也不会返回null
//字符'c'在字符集合[a-z]中且在开头,捕获组(abc | mop)匹配abc或mop,--------
//而后面没有要求要匹配哪些字符
console.log(regex.test('cabc'), regex.test('cmop'), regex.test('cabcKLm'));
//true  true true
console.log(regex.test('Cabc'), regex.test('ckkk'));
//false flase
//如果模式有结尾符号就不一样了, 它要求匹配到字符串最后---------------

//有开头字符要求,也有末尾字符要求,妥妥的要匹配整个字符串
var regex = /^[a-z](abc|mop)$/; 
//对末尾有要求,要求是a到z中的一个+'abc'或'mop'作为结尾的字符串;
var regex1 = /[a-z](abc|mop)$/; 
//第二个字符串多了一个c
console.log(regex.test('cabc'), regex.test('cabcc'));
//true  false

//在末尾匹配上了,开头不被要求是哪些字符;
console.log(regex1.test('cabc'), regex1.test('KALcabc'));
//true true

//exec方法比较复杂,若字符串不能被匹配,则直接返回null,则能匹配,则返回一个数组array,
//array[0]便是匹配到的结果,即被输入字符串的子字符串
//array['index']表示开始被匹配到的字符下标,
//array['input']表示被用来匹配的字符串
//array['groups']表示具名匹配项的键值对
//array数组的其他下标元素表示非具名捕获组匹配到的匹配项,从左到右,从下标1开始
//用上 非捕获、 捕获、具名捕获、非捕获、断言、量词(贪婪和非贪婪)、字符类、Unicode属性转义
//分析以下正则模式: ---------
//(KL|RT|DF)?非贪婪地匹配0个,且生成一个捕获组,此时,值为undefined
//\W非拉丁字母[^a-zA-Z09-9_],此时匹配的是空格
//\b边界字符,两个边界字符间的是一个单词,此时匹配'hello'
//(Bo{2,4}?)捕获组,'o'的次数在2到4之间,少于或多于都不会匹配,量词后?表示尽可能地少次数;
//直接到下一个字符被匹配到
//(?<xiaoMing>Xiao Ming)中(?<xiaoMing>)表示名为'xiaoMing'的具名捕获组
//(?:.{1,}?)dot'.'表示任意一个字符,后面有量词{1,}?,(?:)表示非捕获,
//所以圆括号匹配到的匹配项不会被记录
//\k<xiaoMing>表示引用具名捕获组'xiaoMing'的匹配项'Xiao Ming',当引用不存在的name时,
//会报错, 提示非法具名组名;
//但是当模式中没有任何具名捕获组时,\k<xiaoMing>被当作普通字符;要求被匹配到;
//\4表示第几个捕获组,对应着match结果的下标为n的匹配项,
//在遇到\4时还没有第四个捕获组,所以此时它的值是undefined相当于(?:),
//但是如果模式中没有第4个捕获组,那么使用\4这样的字符可能会报错,提示:
//SyntaxError: Invalid regular expression: Invalid escape
var reg = /abc(KL|RT|DF)?\W\bhello\b.(Bo{2,4}?)\s+!\p{Emoji}\W(?<xiaoMing>Xiao Ming)(?:.{1,}?)(?:\?|!)\k<xiaoMing>\4\s(abc)(?=!)/gu;
console.log(reg.exec('abc hello Booo    !😊 Xiao Ming is running!Xiao Ming abc!'));
//0: "abc hello Booo    !😊 Xiao Ming is running?Xiao Ming abc"
//1: undefined  对应着(KL|RT|DF)?
//2: "Booo"     对应着(Bo{2,4}?)
//3: "Xiao Ming"   对应着(?<xiaoMing>Xiao Ming)的(Xiao Ming)
//4: "abc"          (abc)
//groups: {xiaoMing: "Xiao Ming"}  键'xiaoMing'是(?<name>)中的name,值'Xiao Ming'是其后面的匹配项(Xiao Ming)
//index: 0      从输入字符串的第一个字符匹配到
//input: "abc hello Booo    !😊 Xiao Ming is running?Xiao Ming abc!"
//length: 5

console.log(reg.exec('abc hello Booo    !😊 Xiao Ming is running?Xiao Ming abc?'));
//null     因为'abc'不被感叹号跟随,所以不被匹配
console.log(reg.exec('abc helloBooo    !😊 Xiao Ming is running?Xiao Ming abc!'));
//null    因为'hello'和'Booo'连在了一起,字符串'hello'不是单词;

console.log(reg.exec('abc hello Booooo    !😊 Xiao Ming is running?Xiao Ming abc!'));
//null    因为'Booooo'超过4个'o'

//正则对象还有其他方法和属性,如compile可以动态改变正则的模式:regex.compile(model, flags) ---
//正则对象的属性如 flags,multiline,ignoreCase,global,unicode,source
console.log(regex);
//    /^[a-z](abc|mop)$/
regex.compile('^[^\d]HJKL$', 'g');
console.log(regex, regex.source, regex.flags, regex.multiline, regex.ignoreCase, regex.global, regex.unicode);
//   /^[^d]HJKL$/g "^[^d]HJKL$" "g" false false true false
//以下操作只是说明这些属性是只读的,只有在编写正则对象以及compile的执行时才能改变这些属性---
regex.flags = 'im';
regex.multiline = true;
regex.ignoreCase = true; 
regex.global = false; 
regex.unicode = true;
regex.source = 'abcd'; 
console.log(regex, regex.source, regex.flags, regex.multiline, regex.ignoreCase, regex.global, regex.unicode);
//  /^[^d]HJKL$/g "^[^d]HJKL$" "g" false false true false
//感觉正则对象跟字面量字符串很相似
//正则对象还有一个重要的属性regExp.lastIndex,默认值为0  ----------
//该属性在模式使用全局搜索或粘性匹配,以及使用exec方法搜索才有效,
//它记录下每次exec方法匹配成功后,
//下次的开始检索的位置-----
var reg = /ty/g;
console.log(reg.exec('yytyyytyy'));
console.log(reg.lastIndex);
// ["ty", index: 2, input: "yytyyytyy", groups: undefined]
// 4
console.log(reg.exec('yytyyytyy'));
console.log(reg.lastIndex);
// ["ty", index: 6, input: "yytyyytyy", groups: undefined]
// 8
console.log(reg.exec('yytyyytyy'));
console.log(reg.lastIndex);
// null
// 0
//粘性匹配----
//但是貌似需要从第一个字符就匹配到,
//而且需要连续匹配才能匹配到最后一个,否则会断开就从头开始,就像这样:这跟全局'g'不同
//对应正则对象的属性sticky
var reg = /ty/y;
console.log(reg.exec('yytyyytyy'), reg.lastIndex);
console.log();
// null  0
var reg = /ty/y;
console.log(reg.exec('tytyyytyy'), reg.lastIndex);
//  ["ty", index: 0, input: "tytyyytyy", groups: undefined] 2
console.log(reg.exec('tytyyytyy'), reg.lastIndex);  
//  ["ty", index: 2, input: "tytyyytyy", groups: undefined] 4
console.log(reg.exec('tytyyytyy'), reg.lastIndex);  //不能连续匹配到被断开
//  null 0
console.log(reg.exec('tytyyytyy'), reg.lastIndex); //从头开始
//  ["ty", index: 0, input: "tytyyytyy", groups: undefined] 2

7,String的replace(str | regExp, replaceStr | Function)、match(str | regExp)、matchAll(str | regExp)、search(str | regExp)、split(separator, limit):

//不能确保测试结果没有失误,若发现有误,可以再次测试检验!!!
//match([regExp | str]), 用于匹配正则模式 -------
//当参数是个普通字符串,内部会把该字符串转为一个正则对象
//match方法当不能匹配时返回null,
//当匹配时返回数组,有两种结构形式,一是跟正则对象的exec方法返回值的结构一样,
//二是当match传入一个全局匹配的正则模式,返回值是个包含所有匹配结果的数组而没有捕获组之类的信息
//当不给match传参数时,匹配结果的是个空字串
console.log( 'abcKabc'.match() );
// ["", index: 0, input: "abcKabc", groups: undefined]
//类似于 console.log('abcKabc'.match(/(?:)/)); 
/(?:)/ 是new RegExp('' | undefined | empty)的结果
//empty表示没有传参数

//参数'K'在内部被转为正则对象 new RegExp('K'),即/K/,相当于非全局匹配;
console.log( 'abcKabc'.match('K') );
//["K", index: 3, input: "abcKabc", groups: undefined]

//全局匹配时,
console.log('abcKabc'.match(/K/g));
// ['K']

//matchAll([str | regExp]) , matchAll相当于match的全局匹配模式,------
//但是matchAll可以获得更详细的信息,就像是(/xxx/g).exec(str)方法循环同一个字符串的
//所有匹配结果的集合 --------
//当传入的参数是个正则对象时,要求该正则模式是个全局匹配模式,否则会报错
//当传入的参数是个字符串,会被转为正则对象 new RegExp(str, 'g');
//当没有传入参数时,匹配结果的个数是字符串的长度,每一个结果都类似:
// ["", index: 0, input: "abcKabc", groups: undefined]
//matchAll返回值类型是个迭代器,若有多个结果,可通过多次调用next方法获得所有的结果,
//每一个结果的数据类型都是一个数组,其数据结构都是类似于正则对象exec方法的返回值的结构:
//包含捕获组信息和匹配位置index
//或使用ES6的语法,Array,from()方法和数组扩展运算[...item];
console.log( [...'abcKabc'.matchAll()]); //只是这样在什么情况下有意义呢?
// [[...], [...], [...], [...], [...], [...], [...]]

//当参数是个字符串
console.log( Array.from('abcKabc'.matchAll('abc')));
//[ ["abc", index: 0, input: "abcKabc", groups: undefined], 
//  ["abc", index: 4, input: "abcKabc", groups: undefined] ]

//正则对象
console.log( [...'abcKabc'.matchAll(/abc/g)]);
//[ ["abc", index: 0, input: "abcKabc", groups: undefined],
//  ["abc", index: 4, input: "abcKabc", groups: undefined] ]

//replace([str | regExp, replaceVal | replacer]), -------------
//用于使用新的字符串替换字符串中指定的子字符串
//返回替换后的新字符串
//第一个参数是要被替换的字符串或使用正则模式匹配到的结果作为被替换的字符串,
//若第一个参数是普通字符串,则只会替换一次,替换第一个匹配到的字符串;
//第二个参数可以是普通的数据,作为替换数据,可以是任意数据类型,但都会被字符串化String(XX),
//第二个参数可以是个函数,每一个完整的模式匹配,都会调用该函数,
//该函数的参数个数是个不定数,根据第一个参数而定,
//若是个普通字符串,回调函数的参数会比较简单(match, offset, inputStr)
//若是个正则对象且模式中有捕获组,则参数会有相应捕获组(match, p1, p2..., offset, 
//inputStr, groups)
//第二个参数替换数据可以使用模式匹配到的结果:如何使用??
//$n, 如$1表示第一个捕获组匹配到的匹配项;如果n超出捕获组个数,则被当作普通字符串如'$1'
//$&,表示当前匹配结果
//&`,表示当前匹配结果的左边字符串
//&', 表示当前匹配结果的右边字符串
//$$,表示插入一个'$'符号
//$<name>,表示使用具名捕获组:只有当第一个参数模式有具名捕获组时才有效,
//当使用没有对应具名的名字时,它相当于是一个空字符串;当模式中没有标出任何一个具名捕获组时,
//$<name>会被当作普通字符串

//当第一个参数是普通字符串时,只替换第一个匹配到的子字符串
console.log('HAabcKabcMK'.replace('abc', '$&'), 'HAabcKabcMK'.replace('abc', '$`'), 'HAabcKabcMK'.replace('abc', "$'"), 'HAabcKabcMK'.replace('abc', "$1"));
//HAabcKabcMK  HAHAKabcMK   HAKabcMKKabcMK  HA$1KabcMK

//第二个参数是个普通
console.log('HAabcKabcMK'.replace('abc', 'Xixi'));
//HAXixiKabcMK

//当第二个参数是个函数时
function fn(){
    console.log([...arguments]);
    return ('-' + arguments[0]).toLowerCase();
};
console.log('HAabcKabcMK'.replace('abc', fn));
// [abc, 2, 'HAabcKabcMK'] 依次是匹配结果的字符串,offset(被剋是匹配到的下标),原字符串
// HA-abcKabcMK

//当第一个参数是正则,若是正则不是全局模式,也是只替换第一个匹配到的结果
console.log('HAabcKabcMK'.replace(/abc/, 'Xixi'));
//HAXixiKabcMK

//全局匹配时
console.log('HAabcKabcMK'.replace(/abc/g, '$&'), 
            'HAabcKabcMK'.replace(/abc/g, '$`'), 
            'HAabcKabcMK'.replace(/abc/g, "$'"), 
            'HAabcKabcMK'.replace(/abc/g, "$1"));
//HAabcKabcMK  HAHAKHAabcKMK   HAKabcMKKMKMK  HA$1K$1MK

//有捕获组,有无具名捕获组时
console.log('HAabcKabcMK'.replace(/(abc)(?<test>.?K)/g, '$&'), //
    'HAabcKabcMK'.replace(/(abc)(?<test>.?K)/g, '$2, $1'), //
    'HAabcKabcMK'.replace(/(abc)(?<test>.?K)/g, '+' + '$&'),  //
    'HAabcKabcMK'.replace(/(abc)(?<test>.?K)/g, '$<test>$1'), //
    'HAabcKabcMK'.replace(/(abc)(?<test>.?K)/g, '$3$<na>'), //
    'HAabcKabcMK'.replace(/(abc)(.?K)/g, '$1, $<na>'), //
    'HAabcKabcMK'.replace(/(abc)(.?K)/g, '$`'))
//HAabcKabcMK   'HAK, abcMK, abc'   'HA+abcK+abcMK'  HAKabcMKabc  HA$3$3 
//'HAabc, $<na>abc, $<na>'  HAHAHAabcK

//当而第二个参数是函数时
console.log('HAabcKabcMK'.replace(/(abc)(?<test>.?K)/g, fn))
// ['abcK', 'abc', 'K', 2, 'HAabcKabcMK', {test: 'K'}]  第一次匹配到
// ['abcMK', 'abc', 'MK', 6, 'HAabcKabcMK', {test: 'MK'}]  第二次匹配到
// HA-abck-abcmk

//当没有传参数时,相当于copy源字符串
console.log('HAabcKabcMK'.replace());
//HAabcKabcMK

//search(regExp | str) ,用于搜索指定字符串在源字符串中的起始下标---------
//貌似像是返回的是reg.exec(str)结果的属性index的值;
//如果参数是个普通字符串,则会被转为正则对象
//返回值Number, 若没找到,则返回 -1
//若没有传入参数,相当于search(/(?:)/),结果是0
//默认全局多行检索模式;但只会返回第一个检索到的下标;

//传入普通字符;
console.log('abcTest'.search('ct'), 'abcTest'.search('cT'));
// -1   2
console.log('abcTest'.search(/ct/), 'abcTest'.search(/ct/i));
// -1   2
console.log('abc\nTest'.search(/[^]te/i), 'abc\nTest'.search(/te/i));
// 3     4

//split([separator | regExp, ?limit]),用于使用一个指定的分隔符将字符串分割成多个子字符串,---
//并返回一个字符串数组 
//当没能匹配到分隔符时,会将copy整个字符串作为返回值数组的元素
//充当分割符的字符不会被返回
//第一个参数可以是普通的字符串,也可以是正则表达式
//第二个参数表示限制返回的个数
//当意图使用空字符串分割每个字符时,需要注意字符串中含有表情图形等字符,
//因为这样的字符会被分割成两个字符,貌似跟编码格式有关,表情图形用的编码是Unicode,
//split分割按UTF-16编码,并不是一个Unicode字符都能转为一个UTF-16的字符,可能需要两个?
// '🙂'.split(''); 结果是:["�", "�"]??
//更加详情可以了了解各编码格式之间的关系

//当不传参数,或当参数没有匹配到可用的分隔符,源字符串都会作为返回值的元素
//区分大小写,默认全局检索
console.log('abcKabcMK'.split(), 'abcKabcMK'.split('mk'), 'abcKabcMK'.split(/mk/));
// ['abcKabcMK']   ['abcKabcMK']   ['abcKabcMK']

console.log('abcKabcMK'.split('MK'), 
            'abcKabcMK'.split(/mk/i), 
            '小明去哪玩了?去河里游泳去了'.split('去'));
// ['abcKabc', '']  ['abcKabc', '']  ['小明', '哪玩了?', '河里游泳', '了']

//参数是个空字符 !!!!!
console.log('abcKabcMK'.split(''));
//["a", "b", "c", "K", "a", "b", "c", "M", "K"]

//使用第二个参数 !!!
console.log('小明去哪玩了?去河里游泳去了'.split('去', 2));
//["小明", "哪玩了?"]

参考文档:

Regular Expression

ecma table-binary-unicode-properties

String

使用split的注意事项

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值