RegExp

  1. /pattern/attributes
  2. new RegExp(pattern, attributes);
  3. attributes 表示修饰符
  4. i 表示对大小写不敏感
  5. g 表示搜索全局
  6. m 表示多行匹配
  1. ###修饰符
  2. ###修饰符 描述
  3. i 执行对大小写不敏感的匹配。
  4. g 执行全局匹配(查找所有匹配而非在找到第一个匹配后停止)。
  5. m 执行多行匹配。
  6. ###方括号
  7. ###方括号用于查找某个范围内的字符:
  8. 表达式 描述
  9. [abc] 查找方括号之间的任何字符。
  10. [^abc] 查找任何不在方括号之间的字符。
  11. [0-9] 查找任何从 0 9 的数字。
  12. [a-z] 查找任何从小写 a 到小写 z 的字符。
  13. [A-Z] 查找任何从大写 A 到大写 Z 的字符。
  14. [A-z] 查找任何从大写 A 到小写 z 的字符。
  15. [adgk] 查找给定集合内的任何字符。
  16. [^adgk] 查找给定集合外的任何字符。
  17. (red|blue|green) 查找任何指定的选项。
  18. ###元字符
  19. ###元字符(Metacharacter)是拥有特殊含义的字符:
  20. 元字符 描述
  21. . 查找单个字符,除了换行和行结束符。
  22. \w 查找单词字符。
  23. \W 查找非单词字符。
  24. \d 查找数字。
  25. \D 查找非数字字符。
  26. \s 查找空白字符。
  27. \S 查找非空白字符。
  28. \b 匹配单词边界。
  29. \B 匹配非单词边界。
  30. \0 查找 NUL 字符。
  31. \n 查找换行符。
  32. \f 查找换页符。
  33. \r 查找回车符。
  34. \t 查找制表符。
  35. \v 查找垂直制表符。
  36. \xxx 查找以八进制数 xxx 规定的字符。
  37. \xdd 查找以十六进制数 dd 规定的字符。
  38. \uxxxx 查找以十六进制数 xxxx 规定的 Unicode 字符。
  39. ###量词
  40. ###量词 描述
  41. n+ 匹配任何包含至少一个 n 的字符串。
  42. n* 匹配任何包含零个或多个 n 的字符串。
  43. n? 匹配任何包含零个或一个 n 的字符串。
  44. n{X} 匹配包含 X n 的序列的字符串。
  45. n{X,Y} 匹配包含 X Y n 的序列的字符串。
  46. n{X,} 匹配包含至少 X n 的序列的字符串。
  47. n$ 匹配任何结尾为 n 的字符串。
  48. ^n 匹配任何开头为 n 的字符串。
  49. ?=n 匹配任何其后紧接指定字符串 n 的字符串。
  50. ?!n 匹配任何其后没有紧接指定字符串 n 的字符串。
  51. ###RegExp 对象属性
  52. 属性 描述 FF IE
  53. global RegExp 对象是否具有标志 g 1 4
  54. ignoreCase RegExp 对象是否具有标志 i 1 4
  55. lastIndex 一个整数,标示开始下一次匹配的字符位置。 1 4
  56. multiline RegExp 对象是否具有标志 m 1 4
  57. source 正则表达式的源文本。 1 4
  58. ###RegExp 对象方法
  59. 方法 描述 FF IE
  60. compile 编译正则表达式。 1 4
  61. exec 检索字符串中指定的值。返回找到的值,并确定其位置。 1 4
  62. test 检索字符串中指定的值。返回 true false 1 4
  63. ###支持正则表达式的 String 对象的方法
  64. 方法 描述 FF IE
  65. search 检索与正则表达式相匹配的值。 1 4
  66. match 找到一个或多个正则表达式的匹配。 1 4
  67. replace 替换与正则表达式匹配的子串。 1 4
  68. split 把字符串分割为字符串数组。 1 4

regExp.prototype.compile(regExp)

regExp 新的正则
返回void
方法改变正则表达式

  1. var str="Every man in the world! Every woman on earth!";
  2. patt=/man/g;
  3. str2=str.replace(patt,"person");
  4. document.write(str2+"<br />");
  5. patt=/(wo)?man/g;
  6. patt.compile(patt);
  7. str2=str.replace(patt,"person");
  8. document.write(str2);

regExp.prototype.exec(str)

str 被检查的字符串
返回void
检查到返回正则检查到的内容,并改变regExp.lastIndex的位置
执行正则检索字符串

  1. <html>
  2. <body>
  3. <div id="nodeDiv"></div>
  4. <script type="text/javascript">
  5. var str = "Visit W3School, W3School is a place to study web technology.";
  6. var patt = new RegExp("W3School","g");
  7. var result;
  8. while ((result = patt.exec(str)) != null) {
  9. document.write(result + ":" + patt.lastIndex);
  10. document.write("<br />");
  11. }
  12. </script>
  13. </body>
  14. </html>
  15. W3School:14
  16. W3School:24

regExp.prototype.test(str)

str 被检查的字符串
返回boolean
检查字符串是否具备匹配正则表达式条件,能匹配上就返回true,否则false

  1. var str = "Visit W3School";
  2. var patt1 = new RegExp("W3School");
  3. var result = patt1.test(str);
  4. document.write("Result: " + result);

调用 RegExp 对象 r 的 test() 方法,并为它传递字符串 s,与这个表示式是等价的:(r.exec(s) != null)。

string.prototype.search(regExp)

regExp 一个正则表达式
返回检索到的字符串的下标,没找到返回-1
search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置。

  1. var str = ' abc abc abc';
  2. var regExp = /abc/
  3. var index = str.search(regExp);
  4. console.log(index); // 1

string.prototype.match(stringValue | regExp);

regExp 一个正则表达式 或者 stringValue 一个字符串
返回检索到的值的数组,存放匹配结果的数组。该数组的内容依赖于 regexp 是否具有全局标志 g
字符串内检索指定的值,或找到一个或多个正则表达式的匹配

  1. var str = ' abc abc abc';
  2. var regExp = /abc/g
  3. var arr = str.match(regExp);
  4. console.log(arr); // ['abc', 'abc', 'abc'];
  5. 如果正则不带g的话,返回如下
  6. ["abc", index: 1, input: " abc abc abc", groups: undefined]

string.prototype.split(stringValue | regExp, maxLength)

regExp 一个正则表达式 或者 stringValue 一个字符串,maxLength 返回的数组最大长度
返回一个数组
将字符串按指定的值拆分成一个数组

  1. var str = ' abc abc abc';
  2. var regExp = /\s/;
  3. var a = str.split(regExp);
  4. var b = str.split(regExp, 2);
  5. console.log(a, b);
  6. // ["" , "abc", "abc", "abc"], ["", "abc"]

string.prototype.replace(stringValue | regExp, newString)

regExp 一个正则表达式 或者 stringValue 一个字符串, newString 新的字符串
返回一个新的字符串
字符串替换

  1. var str = ' abc abc abc';
  2. var regExp = /abc/;
  3. var regExpAll = /abc/g;
  4. var a = str.replace(regExp, 'bbb');
  5. var b = str.replace(regExpAll, '222');
  6. console.log(a, b);
  7. // ' bbb abc abc',' 222 222 222'

正则使用规则说明

注意区分[\s\S]与.的区别,. 查找单个字符,除了换行和行结束符。

  1. [\s\S] 表示匹配任意字符
  2. [abc] 表示匹配abc

? 有两个用法,匹配一个或零个 || 贪婪模式与非贪婪模式

  1. 匹配一个或零个
  2. 比如https?匹配的https(一个s)或者http(零个s
  3. 贪婪匹配:尽可能匹配到多的内容
  4. 非贪婪模式,所谓非贪婪模式,就是匹配尽可能少的内容,比如,对于源字符串
  5. 如:var str="abcaxc";
  6. var p1= /ab(.*)c/; // abcaxc
  7. var p2= /ab(.*?)c/;// abc
  8. 贪婪摸索匹配abcaxc str.match(p1)
  9. 非贪婪模式匹配abc str.match(p2)
  10. 如:
  11. var str1 = '<div>a</div><div>b</div>' // (注意:如果源字符串有换行,使用[\s\S]替换 . ):
  12. var p1 = /<div>(.*)<\/div>/g // 只得到一个结果,贪婪模式,会找到最后一个</div>
  13. var p2 = /<div>(.*?)<\/div>/g // 得到两个结果,非贪婪模式
  14. console.log(str1.match(p1)) // ["<div>a</div><div>b</div>"]
  15. console.log(str1.match(p2)) // ["<div>a</div>", "<div>b</div>"]
  16. var p1 = /<div>(.*)<\/div>/ // 不带全局搜索的情况
  17. var p2 = /<div>(.*?)<\/div>/ // 不带全局搜索的情况
  18. console.log(str1.match(p1)) //
  19. [
  20. 0: "<div>a</div><div>b</div>"
  21. 1: "a</div><div>b"
  22. groups: undefined
  23. index: 0
  24. input: "<div>a</div><div>b</div>"
  25. ] // length: 2
  26. console.log(str1.match(p2)) //
  27. [
  28. 0: "<div>a</div>"
  29. 1: "a"
  30. groups: undefined
  31. index: 0
  32. input: "<div>a</div><div>b</div>"
  33. ] // length: 2
  34. 需求:匹配1后面跟任意个0
  35. 源串:10001
  36. 使用贪婪模式:10* 结果:1000 1
  37. 使用非贪婪模式:10*? 结果:1 1
  38. 需求:匹配1后面跟任意个0,再跟一个1
  39. 源串:10001
  40. 使用贪婪模式:10*1 结果:10001
  41. 使用非贪婪模式:10*?1 结果:10001

多选一 |

  1. (http|https|ftp|svn)abc

数据分组与取数据

对于要重复单个字符,非常简单,直接在字符后卖弄加上限定符即可,例如 a+ 表示匹配1个或一个以上的a,a?表示匹配0个或1个a。但是我们如果要对多个字符进行重复怎么办呢?此时我们就要用到分组,我们可以使用小括号”()”来指定要重复的子表达式,然后对这个子表达式进行重复,例如:(abc)? 表示0个或1个abc 这里一 个括号的表达式就表示一个分组 。
分组可以分为两种形式,捕获组和非捕获组

捕获组 \1与$1的使用介绍

**捕获组可以通过从左到右计算其开括号来编号 。例如,在表达式 (A)(B(C)) 中,存在四个这样的组:

0 (A)(B(C))
1 (A)
2 (B(C))
3 (C)

0始终表示整个表达式
Back 引用 是说在后面的表达式中我们可以使用组的编号来引用前面的表达式所捕获到的文本序列。
注意:反向引用,引用的是前面捕获组中的文本而不是正则,也就是说反向引用处匹配的文本应和前面捕获组中的文本相同,这一点很重要。
例子

  1. (["']).*\1
  2. 其中使用了分组,\1就是对引号这个分组的引用,它匹配包含在两个引号或者两个单引号中的所有字符串,
  3. 如,"abc" 或 " ' " 或 ' " ' ,但是请注意,它并不会对" a'或者 'a"匹配。
  4. 原因上面已经说明,Back引用只是引用文本而不是表达式。

(http|ftp|svn)://([^/]+),分组1得到的是(http|ftp|svn)里面的数据,分组2得到([^/]+)里面的数据,对于嵌套括号也是点左括号即可。在正则中有很多与括号结合的写法,你在数左括号的时候,一定要注意,非捕获组和环视的左括号都是不需要数的。
在使用子模式过程中,常见两种写法是:\1$1
(1) \1 是在正则表达式本身中引用分组1的内容,如:
我们要匹配111这样的连续出现3此的数字,我们可以写出正则:(\d)\1\1(\d)匹配到第一个1,后面再引用这个匹配内容,得到111
(2) $1 是在替换中调用分组的内容,如:
我们要替换链接参数name=Zjmainstayusername=Zjmainstay,我们可以使用正则name=([^&]+)替换为username=$1来实现,这里的$1就引用了分组1的结果Zjmainstay,因此得到我们想要的结果。

非捕获组

上面说到()作为子模式可以得到它里面的数据,但是,有些时候,()只是作为数据分界功能,并不需要取出来,这时候就要用到非捕获组的概念了。比如:(http|ftp|svn)://([^/]+)只想得到域名,也就是[2],那么(http|ftp|svn)就只是数据分界的功能,这里不需要捕获,因此使用非捕获组功能,(?:http|ftp|svn)屏蔽这部分的数据获取,此时,(?:这个左括号排除[1]计数,也就是(?:http|ftp|svn)://([^/]+)中的([^/]+)变成[1]了。
非捕获组有很多种形式,其中包括:零宽度断言和模式修正符

零宽度断言,注意左侧检测有浏览器兼容问题

(?=X) 零宽度正先行断言。仅当子表达式 X 在 此位置的右侧匹配时才继续匹配。也就是说要使此零宽度断言起到我们想要的效果的话,就必须把这个非捕获组放在整个表达式的右侧。例如,/w+(?=/d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。
(?!X) 零宽度负先行断言。仅当子表达式 X 不在 此位置的右侧匹配时才继续匹配。例如,例如,/w+(?!/d) 与后不跟数字的单词匹配,而不与该数字匹配 。
(?<=X) 零宽度正后发断言。仅当子表达式 X 在 此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
(?<!X) 零宽度负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如,(?<!19)99 与不跟在 19 后面的 99 的实例匹配
(?:X) 与(?=X)用法一致,但是它将包含X,而(?=X)不包含X

例子

  1. var a = ['aaa222', 'aaa', 'bbb1' ]
  2. var regexp = /[A-z]+(?=\d)/; // 匹配后面跟着数字的[A-z]
  3. console.log( regexp.exec(a[0]) ); // 'aaa'
  4. console.log( regexp.exec(a[1]) ); // null
  5. console.log( regexp.exec(a[2]) ); // 'bbb'
  6. var regexp1 = /[A-z]+(?!\d)/; // 匹配后面跟着数字的[A-z]
  7. console.log( regexp1.exec(a[0]) ); // 'aa'
  8. console.log( regexp1.exec(a[1]) ); // 'aaa'
  9. console.log( regexp1.exec(a[2]) ); // 'bb'
  10. var b = ['222aaa', 'aaa', '1bbb'];
  11. var regexp2 = /(?<=\d)[A-z]+/; // 匹配前面跟着数字的[A-z]
  12. console.log( regexp2.exec(b[0]) ); // 'aaa'
  13. console.log( regexp2.exec(b[1]) ); // null
  14. console.log( regexp2.exec(b[2]) ); // 'bbb'
  15. var regexp3 = /(?<!\d)[A-z]+/; // 匹配前面不跟着数字的[A-z]
  16. console.log( regexp3.exec(b[0]) ); // 'aa'
  17. console.log( regexp3.exec(b[1]) ); // 'aaa'
  18. console.log( regexp3.exec(b[2]) ); // 'bb'
  19. // 正则表达式 (?<!4)56(?=9) 匹配前面不是4开头的但是是9结尾的字符
  20. var c = [ 7569, 4568, 4569, 5567 ];
  21. var regexp4 = /(?<!4)56(?=9)/;
  22. console.log( regexp4.exec(c[0]) ); // '56'
  23. console.log( regexp4.exec(c[1]) ); // null
  24. console.log( regexp4.exec(c[2]) ); // null
  25. console.log( regexp4.exec(c[3]) ); // null
  26. // 提取字符串 da12bka3434bdca4343bdca234bm 中包含在字符a和b之间的数字
  27. // ,但是这个a之前的字符不能是c;b后面的字符必须是d才能提取。
  28. // 首先写出 [^c]a\d*bd
  29. // (?<=[^c]a)\d+(?=bd)

模式修正符

以(?)开头的非捕获组除了零宽度断言之外,还有模式修正符。
正则表达式中常用的模式修正符有i、g、m、s、x、e等。它们之间可以组合搭配使用。

  1. [例1](?i)ab
  2. 表示对(?i)后的所有字符都开启不区分大小写的开关。故它可以匹配abaBAbAB
  3. [例2](?i:a)b
  4. 它表示只对a开启不区分大小写的开关。故它可以匹配abAb。不能匹配aBAB

(?>Pattern)等同于侵占模式,

注意javascript中不允许侵占模式的语法/\d++/ , /(?>[1-9]?)/ 都不允许
贪婪模式与侵占模式的比较
正则:\w+[a-z]与\w++[a-z]
目标串:232hjdhfd7474$
分析:①\w+[a-z]:\w+属于贪婪模式,会一次性吃掉它所能吃掉的所有的字符,也就是子串232hjdhfd7474,此时[a-z]不能够找到匹配了,故\w+匹配的串会吐出一个字符4,但此时还是得不到匹配。反复的这样吐出回退,直到吐出字符d时,此时[a-z]能够匹配h,所以这时正则表达式会返回一次成功的匹配结果,为232hjdhfd
②\w++[a-z]:\w++属于侵占模式,它会一次性吃掉它所能够吃掉的所有字符,即子串232hjdhfd7474,而且不留给其他部分使用,故不会回退。此时[a-z]不能够找到匹配,所以此次匹配失败。在余下的子串中也找不到能匹配成功的子串。所以整个正则表达式是找不到匹配结果的!

  1. 【例】将一些多位的小数截短到三位小数:\d+\.\d\d[1-9]?\d+
  2. 在这种条件下 6.625 能进行匹配,这样做没有必要,因为它本身就是三位小数。最后一个“5”本来是给 [1-9] 匹配的,但是后面还有一个 \d+ 所以,[1-9] 由于是“?”可以不匹配所以只能放弃当前的匹配,将这个“5”送给 \d+ 去匹配,如果改为:
  3. \d+\.\d\d[1-9]?+\d+
  4. 的侵占形式,在“5”匹配到 [1-9] 时,由于是侵占式的,所以不会进行回溯,后面的 \d+ 就匹配不到任东西了,所以导致 6.625 匹配失败。
  5. 这种情况,在替换时就有效了,比如把数字截短到小数点后三位,如果正好是三位小数的,就可以不用替换了,可以提高效率,侵占量词基本上就是用来提高匹配效率的。
  6. \d+\.\d\d[1-9]?+\d+ 改为 \d+\.\d\d(?>[1-9]?)\d+ 这样是一样的。
量 词 种 类 意  义
贪婪 非贪婪模式 侵占
X? X?? X?+ 匹配 X 零次或一次
X* X*? X*+ 匹配 X 零次或多次
X+ X+? X++ 匹配 X 一次或多次
X{n} X{n}? X{n}+ 匹配 X n 次(这个应该不存在这几种模式,就是固定匹配n个)
X{n,} X{n,}? X{n,}+ 匹配 X 至少 n 次
X{n,m} X{n,m}? X{n,m}+ 匹配 X 至少 n 次,但不多于 m 次

正则实战题目

脱壳对比长度

  1. [
  2. {"type":"ar","key_name":"i18n OFF","oldValue":"خصم <br>{0}","newValue":"خصم{0}{1}<br>Clearance"},
  3. {"type":"de","key_name":"i18n OFF","oldValue":"{0}<br> OFF","newValue":"{0}{1}OFF<br>Clearance"},
  4. {"type":"en","key_name":"i18n OFF","oldValue":"{0}<br> OFF","newValue":"{0}{1}OFF<br>Clearance"},
  5. {"type":"es","key_name":"i18n OFF","oldValue":" {0}<br> OFF","newValue":"{0}{1}OFF<br>Clearance"},
  6. {"type":"fr","key_name":"i18n OFF","oldValue":"{0}<br> OFF","newValue":"{0}{1}OFF<br>Clearance"},
  7. {"type":"it","key_name":"i18n OFF","oldValue":"{0}<br> OFF","newValue":"{0}{1}OFF<br>Clearance"},
  8. {"type":"pt","key_name":"i18n OFF","oldValue":"{0}<br> OFF","newValue":"{0}{1}OFF<br>Clearance"}
  9. ].filter(item => {
  10. const p1 = /{(.*?)}/g
  11. const arrA = item.oldValue.match(p1)
  12. const arrB = item.newValue.match(p1)
  13. if (arrA === null && arrB === null) {
  14. return false
  15. }
  16. if (!arrA || !arrB) {
  17. return true
  18. }
  19. return item.oldValue.match(p1).length !== item.newValue.match(p1).length
  20. })

去掉头尾空格

  1. (' sss sss sss ').replace(/(^\s*)|(\s*$)/g, '');

去掉中间空格

  1. 方法一
  2. (' sss sss sss ').replace(/(?<=\S)\s+(?=\S)/g, '');
  3. 方法二
  4. (' sss sss sss ').replace(/\s+(?=\S)/g, function(){
  5. var arr = Array.prototype.slice.call(arguments);
  6. if (arr[1] === 0) {
  7. return arr[0];
  8. }
  9. return '';
  10. });

计算标题和文字长度

  1. function getNickNameLength(id){
  2. var title = document.querySelecter(`#${id}`);
  3. var len = 0;
  4. for (var i = 0; i < title.length; i++) {
  5. var a = title.charAt(i);
  6. if (a.match(/[^\x00-\xff]/ig) != null){
  7. len += 2;
  8. }else{
  9. len += 1;
  10. }
  11. }
  12. return len;
  13. }

特定语法匹配替换=g文字文字=

说明:匹配字符串中形如 =g文字文字= 的语法,并将相应部分转化为对应的标签文字文字
示例:
transform(‘=g1.18 进入开发=’); // 1.18 进入开发
transform(‘=g1.23 联调(-1)=,=g1.25 发布(+1)=’);// 1.23 联调(-1)1.25 发布(+1)
transform(‘1.25 发布’); // 1.25 发布

  1. function transform(str) {
  2. var regExp = /=g[^=]*=/g
  3. var strA = '';
  4. var resule;
  5. var previous = 0;
  6. while((result = regExp.exec(str)) != null) {
  7. var temp = result[0].replace('=g', '<g>').replace('=', '</g>');
  8. // 要替换的字符串的下标在 index到regExp.lastIndex
  9. var index = regExp.lastIndex - result[0].length;
  10. strA += str.slice(previous, index) + temp;
  11. previous = index + result[0].length;
  12. }
  13. console.log(previous, str.length)
  14. if (strA && previous < str.length) {
  15. strA += str.slice(previous, str.length);
  16. }
  17. return strA ? strA : str;
  18. }
  19. console.log(transform('=g1.18 进入开发= . =g1.18 进入开发= . ss =g ='))
  20. console.log(transform('sdsadas =g1.18 进入开发= . =g1.18 进入开发= . ss =g = ====ss'))
  1. // 大佬给的新方法
  2. var a = 'aaa=g1.23 联调(-1)=,=g1.25 发布(+1)=bbb';
  3. function transform(str) {
  4. var results = a.replace(/=g([^=]+)=/g, function() {
  5. return '<g>' + arguments[1] + '</g>'
  6. });
  7. return results;
  8. }
  9. console.log(transform(a));

image.png

合并数组中相邻且重复的元素

说明:请实现一个函数 merge,传入一个数组,合并数组中【相邻且重复】的元素。
示例:
merge([3,2,2,4,5,5,6,2,1]); // 输出[3,2,4,5,6,2,1]
merge([3,2,3]); // 输出[3,2,3]
merge([2,2,3]); // 输出[2,3]

  1. function merge(arr) {
  2. return (arr + ',').replace(/(\d+,)\1+/ig, '$1').split(',').slice(0, -1);
  3. }
  4. {} + ',' // NaN
  5. [1,2,3, {}] + ',' // "1,2,3,[object Object],"

按规范输出内容

  1. var str = "4[2[10[a]5[b]]]"; // 要求输出2个10个a和5个b组成的字符串
  2. var getResult = (s) => {
  3. let reg=/(\d+)\[(\w+)\]/g;
  4. let str = s.replace(reg,(total,word1,word2, word3, data, list)=>{
  5. console.log(total,word1,word2, word3, data, list);
  6. return new Array(Number(word1)).fill(word2).join('')
  7. })
  8. console.log(str)
  9. return reg.test(str) ? this.getResult(str) : str
  10. }
  11. console.log(getResult(str));