Go to comments

javascript 正则表达式学习笔记

正则表达式(Regular Expression 在 js 代码中简写为 regexp)

重点它是一个表达式,正则是修饰表达式的修饰词,意思是符合一个规则的表达式,

主要作用是对字符串进行操作、过滤、以及检索校验的一种规则


一、定义和匹配

通过一个“用户名规则的校验”体验,正则表达式是如何定义和匹配的


定义“正则表达式”的两种方式

1. 直接字面量 /abc/

2. 构造函数 new RegExp('abc')


正则表达式使用的匹配方法

1. 正则表达式上的方法 reg.test(str)

2. 字符串上的方法 str.match(reg)


示例,

输入的用户名,分别是以数字、字母或下划线开头

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>用户名的校验规则</title>
</head>
<body>

<input type="text" id="username"/>
<div id="msg"></div>

<script>

  var username = document.getElementById('username');
  var msg = document.getElementById("msg");

  username.oninput = function(){
    checkedFun(this.value);
  }

  function checkedFun(str){
    var reg = /^[\dA-z_]/;
    if(str.match(reg)){
      msg.innerText = "符合规则";
    }else{ 
      msg.innerText = "用户名只能以数字、字母、下划线开头";
    }
  }
  
</script>
</body>
</html>


监听 oninput 输入事件

获取 input 框输入的值是一个字符串,正则表达式对输入的字符串进行校验


 ^[\dA-z_] 

方括号表示一个字符,字符可以是数字 \d、可以是字母 A-z 或下划线 _

^ 表示以方括号里面的字符开头


str.match(reg)

使用字符串的 match 方法实现正则的匹配

1. 输入 abc 匹配成功,返回的是一个数组 ['a', index: 0, input: 'abc', groups: undefined]

2. 输入空格或中文返回 null 表示不符合匹配规则


 用构造函数 RegExp 的方式声明正则表达式,参数传一个字符串是匹配规则

function checkedFun(str){
  var reg = new RegExp('^[\\da-zA-Z_]');
  if(reg.test(str)){
    msg.innerText = "符合规则";
  }else{ 
    msg.innerText = "用户名只能以数字、字母、下划线开头";
  }
}


注意,

 new RegExp('^[\\dA-z_]') 

参数是字符串,在字符串中 \ 斜杠有特殊的含义,表示转义后面的一个字符,

要想实现 \d 的效果,要在加一个斜杠 \\d 转义一下,表示取消后面第二个 \ 斜杠的含义


reg.text(str)

正则表达式上的匹配方法

1. 参数是要校验的字符串,字符是否符合规则

2. 返回一个布尔值


一些匹配规则

/[3578]$/   以中括号里面任何一个数字结尾

/[3578]+$/   至多个任意数字结尾,+号表示1~多个

/^[3578]+$/   整个字符串就是这样的 3578

/3578/   整个字符串中含一个整体的 3578


Ps:

input 元素是行级块元素,既可以设置宽高也不会占满一行,修改一下 .msg 元素 display: inline-block 就到一行了


二、原子

什么是原子?

组成正则表达式的最小单位就是原子


原子表

语法描述
[abc]方括号内的任意一个字符
[^abc]不在方括号内的字符
[0-9]0-9 之间的数字
[a-z]任何小写字母
[A-Z]任何大写字母
[A-z]大写和小写字母
()子表达式
\uxxx查找十六进制数 xxx 规定的 Unicode 字符
[\u4e00-\u9fa5]所有中文字符
参考图片连接


元字符

语法描述
.代表单个字符,除了换行符和行结束符以外的任意一个字符
\w小写 w 表示字母、数字、下划线,等价于 [A-Za-z0-9_]
\W非单词字符,就是除了小 \w 以外的字符
\d数字
\D非数字
\s空白字符
\S非空白字符
\b单词边界
\B非单词边界符
\n换行符
\f换页符
\r回车符
\t制表符
\v垂直字表符


模式修饰符

修饰符
i代表 ignorecase 忽略大小写
m代表 multiline 多行匹配
g代表 global 全局匹配,匹配出所有符合规则的子字符串


. 点

正则表达式中“点”有特殊的含义,表示任意一个字符,除了换行符和行结束符以外,其他的任意一个字符

换行符是 \n

行结束符就是回车 \r

也就是说 \n 和 \r 它匹配不出来


/.ello/ 以任意一个字符开始,后面必须是 ello

var str = "hello world, hello duyi, welcome duyi";

var reg = /.ello/g;

console.log(str.match(reg)); //  ['hello', 'hello']


修饰符 g 全局匹配,表示把所有符合条件的全部匹配出来


字符串的 match 方法返回是所有符合条件的“子字符串”是一个数组的形式

加了 g 返回的是数组

如果没有加 g 返回的是类数组

var str = "hello world, hello duyi, welcome duyi";

var reg = /.ello/;

console.log(str.match(reg)); // ['hello', index: 0, input: 'hello world, hello duyi, welcome duyi', groups: undefined]

// [
//   0: "hello" // 匹配成功的字符片段
//   groups: undefined // groups代表一个组,这个组代表一个子表达式,没有就是undefined
//   index: 0 // 当前匹配成功的索引值
//   input: "hello world, hello duyi, welcome duyi" // 我们要匹配的整个字符串
//   length: 1
// ]


点不能匹配换行符 \n 和结束符 \r,可以匹配出制表符 \t

var str = "第一行\nello world, \rello duyi, \tello lizi";

var reg = /.ello/g;

console.log(str.match(reg)); // ['\tello']


alert(str) 弹框里面可以看到换行符、回车符的效果分成了三行,制表符的是一个空白

第一行

ello world, 

ello duyi, ello lizi


\w

小写 w 代表大小英文的字母、数字、下划线,

单纯的一个 \w 返回了所有的英文字符,但不包括空格、制表符、换行符、行结束符,逗号

var str = "\rello world, \nello duyi, \tello lizi";

var reg = /\w/g;

console.log(str.match(reg)); // ['e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'e', 'l', 'l', 'o', 'd', 'u', 'y', 'i', 'e', 'l', 'l', 'o', 'l', 'i', 'z', 'i']


量词

+ 加号,表示匹配前一个字符的 1 至 多个

* 星号,代表前一个字符的 0 至 多个


元字符 \w 结合量词 + 加号,可以拆分字符串中一个一个的单词,不要逗号、空格、制表符

var str = "hello world, hello duyi, welcome duyi \tello";

var reg = /\w+/g;

console.log(str.match(reg)); // ['hello', 'world', 'hello', 'duyi', 'welcome', 'duyi', 'ello']


正则表达式的规则一般遵循“贪婪匹配”就是越多越好,

所以量词 + 号是有几个就匹配几个,不是返回单个的字母了


* 星号表 0 至 多个,也就是说没有的也能给匹配出来,

匹配出好多个空位,代表 0 个英文字符,

还有空逗号、制表符都匹配成空位了

var str = "hello world, hello duyi, welcome duyi \tello";

var reg = /\w*/g;

console.log(str.match(reg)); // ['hello', '', 'world', '', '', 'hello', '', 'duyi', '', '', 'welcome', '', 'duyi', '', '', 'ello', '']


\W

大写 W 匹配非单词字符,空格、逗号、制表符

var str = "\rello world, \nello \tduyi";

var reg = /\W/g;

console.log(str.match(reg)); // ['\r', ' ', ',', ' ', '\n', ' ', '\t']

// 0: "\r"
// 1: " "
// 2: ","
// 3: " "
// 4: "\n"
// 5: " "
// 6: "\t"


\d

小写 d 代表数字,数字一个一个的匹配出来了

var str = "hello world123, hello456 duyi, welcome duyi \tello132323";

var reg = /\d/g;

console.log(str.match(reg)); // ['1', '2', '3', '4', '5', '6', '1', '3', '2', '3', '2', '3']


配合量词 + 号匹配出连着的数字

var str = "hello world123, hello456 duyi, welcome duyi \tello132323";

var reg = /\d+/g;

console.log(str.match(reg)); // ['123', '456', '132323']


\D

大写 D 代表非数字,配合量词 + 号,除数字以外的其他字符都匹配出来了

var str = "hello world123, hello456 duyi, welcome duyi \tello132323";

var reg = /\D+/g;

console.log(str.match(reg)); // ['hello world', ', hello', ' duyi, welcome duyi \tello']

// 0: "hello world"
// 1: ", hello"
// 2: " duyi, welcome duyi \tello"

相当于数字充当了分割符


\s

小写 \s 表示空白字符,其实就是空格,包含 \r、\n、\t

因为有量词 + 号,\n 连着前面的空格

var str = "hello world\r, hello duyi, \nwel comeduyi\t";

var reg = /\s+/g;

console.log(str.match(reg)); // [' ', '\r', ' ', ' ', ' \n', ' ', '\t']

// 0: " "
// 1: "\r"
// 2: " "
// 3: " "
// 4: " \n"
// 5: " "
// 6: "\t"
// length: 7


\S

大写 S 非空白字符,没有 \r、\n、\t

var str = "hello world, hello duyi,\r welcome \nduyi \tello132323";

var reg = /\S+/g;

console.log(str.match(reg)); //  ['hello', 'world,', 'hello', 'duyi,', 'welcome', 'duyi', 'ello132323']


\b

匹配单词边界,每一个单词有两个边界,七个单词有14个空白

var str = "hello world, hello duyi,\r welcome \nduyi \tello";

var reg = /\b/g;

console.log(str.match(reg)); // ['', '', '', '', '', '', '', '', '', '', '', '', '', '']


\B

非单词边界就是两个挨着的字母之间

var str = "hello world";

var reg = /\B/g;

console.log(str.match(reg)); // ['', '', '', '', '', '', '', '']


单词边界和空格的区别

1. 空格有 " " 空字符

2. 整个字符如果连着 -hello- 左右两边是单词边界,也就是说一个单词有两个边界

3. 非单词边界就 h-e-l-l-o 字母之间

var str = "hello";

var reg = /\b/g;
console.log(str.replace(reg, '-')); // -hello-

var reg = /\B/g;
console.log(str.replace(reg, '-')); // h-e-l-l-o


三、量词

量词是用来修饰前面一个字符的个数,就是你想匹配多少个

量词描述
n+1次多次
n*0次1次多次
n?0次1次
n{x}前一个字符固定的个数
n{x,y}代表一个范围
n{x,}
n$
^n
?=n正向肯定预查,m(?=n)  匹配任何后面紧接着指定字符串 n 的字符串
?!n正向否定预查,m(?!n)  匹配后面不是 n 的 m
?<=n反向肯定预查,(?<=n)m 匹配前面是 n 的 m
?<!n反向否定预查,(?<!n)m 匹配前面不是 n 的 m


? 号

? 号只匹配 0 或 1 个,可以取消贪婪匹配


把英文字母一个一个的拿出来,只取字母不要空格,正常的做法是

1. 全局 g 匹配

2. [A-z] 包括大小写字母

3. 字符串的 match 方法返回一个数组

var str = "hello world";

var reg = /[A-z]/g;

console.log(str.match(reg)); // ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']


+号、*号都是贪婪匹配,就是有多少匹配多少

加上量词 + 号表示 1 次至多次,可以匹配出字符串中的两个单词

var str = "hello world";

var reg = /[A-z]+/g;

console.log(str.match(reg)); // ['hello', 'world']


这是正则的贪婪匹配的效果,

就是有多少符合条件的就都匹配出来作为一个串,能匹配多就不匹配少


加上 ? 问号,表示“非贪婪匹配”能少就不多,匹配结果又是一个一个的字母了

var str = "hello world";

var reg = /[A-z]+?/g;

console.log(str.match(reg)); // ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']


{} 花括号

传一个数字表示前面字符的个数

var str = "hello world";

var reg = /[A-z]{3}/g;

console.log(str.match(reg)); //  ['hel', 'wor']


传两个数字表示范围

var str = "hello world";

var reg = /[A-z]{2,4}/g;

console.log(str.match(reg)); //  ['hell', 'worl']


注意,

两个数字 {2,4} 之间是逗号隔开,并且不能有空格


把 {2, 4} 放到字符里面,有空格就不是范围了,就当做字符匹配了

var str = "hello world{2, 4}";

var reg = /[A-z]{2, 4}/g;

console.log(str.match(reg)); // ['d{2, 4}']


?!n 正向否定预查


a(?!d)

意思是希望匹配的是 a,条件是匹配的 a 后面不能是 d

字符串里面有三个 a,匹配出两个后面两个 a

var str = "adkhfalskdfa";

var reg = /a(?!d)/g;

console.log(str.match(reg)); // ['a', 'a']


?<=n 反向肯定预查


(?<=f)a

我们想匹配的 a ,条件是前面是 f 后面的那个 a

var str = "adkhfalskdfa";

var reg = /(?<=f)a/g;

console.log(str.match(reg)); // ['a', 'a']


?<!n 反向否定预查


(?<!f)a

匹配的是 a,但是 a 的前面不能是 f,只有第一个 a 满足条件

var str = "adkhfalskdfa";

var reg = /(?<!f)a/g;

console.log(str.match(reg)); // ['a']


四、子表达式

子表达式可以单独匹配,并将匹配成功的结果在整个正则表示中单独的引用


有这样一个要求,

匹配出三个相同的字符,比如 aaa、bbb、eee


/(\w){3}/ 相当于 /(\w\w\w)/,而每个 \w 有自己的范围,代表范围内不同的字母,最后一个 dde 不符合要求

var str = "aaabbbddeee";

var reg = /(\w){3}/g;

console.log(str.match(reg)); // ['aaa', 'bbb', 'dde']


\1 叫反向引用,是子表达式的编号,

代表第一个子表达式匹配出来的内容,

就是把子表达式匹配的内容拿出来用,所以匹配的内容跟第一个表达式相同

var str = "aaabbbddeee";

var reg = /(\w)\1/g;

console.log(str.match(reg)); // ['aa', 'bb', 'dd', 'ee']


匹配三个相同的字符就写两个 (\w)\1\1,

后面两个 \1 都是第一个子表达式 {\w} 匹配成功的字符

var str = "aaabbbddeee";

var reg = /(\w)\1\1/g;

console.log(str.match(reg)); // ['aaa', 'bbb', 'eee']


子表达式的“反向引用”在字符串替换方法 replace 里面的应用

参数一,传的是正则表达式

参数二,如果传递的是固定的字符串,会把符合匹配规则的部分进行替换

var str = "aaabbbddeee";

var reg = /(\w)\1\1/g;

console.log(str.replace(reg, '*')); // **dd*


replace 方法的第二个参数还可以传一个函数,通过 arguments 打印函数里面的参数

var str = "aaabbbddeee";

var reg = /(\w)\1\1/g;

str.replace(reg, function(){
  console.log(arguments);
});

// Arguments(4) ['aaa', 'a', 0, 'aaabbbddeee', callee: ƒ, Symbol(Symbol.iterator): ƒ]
// Arguments(4) ['bbb', 'b', 3, 'aaabbbddeee', callee: ƒ, Symbol(Symbol.iterator): ƒ]
// Arguments(4) ['eee', 'e', 8, 'aaabbbddeee', callee: ƒ, Symbol(Symbol.iterator): ƒ]

每一次打印一个数组

[

  0: aaa, 是匹配成功的字符

  1: a, 第一个子表达式 (\w) 匹配成功的内容

  2: 0, 是一个索引值,当前匹配成功的位置

]


写上三个形参,打印成功匹配的结果

var str = "aaabbbddeee";

var reg = /(\w)\1\1/g;

str.replace(reg, function(param1, param2, param3){
  console.log(param1, param2, param3);
});

// aaa a 0
// bbb b 3
// eee e 8


做一个字符串去重,

正则规则 /(\w)\1+/ 匹配1至多个重复的字母,

函数里面 return 的内容,就是替换的内容,

所以函数的第二个参数是子表达式匹配成功的内容,直接返回第二个参数 param2 就可以了

var str = "Aaadddoooooooobbeee";

var reg = /(\w)\1+/gi;

var result = str.replace(reg, function(param1, param2, param3){
  return param2;
});

console.log(result); // Adobe


如果是两个 /1,替换重复的字符

var str = "Aaadddoooooooobbeee";

var reg = /(\w)\1\1/gi;

var result = str.replace(reg, function(param1, param2, param3){
  return param2;
});

console.log(result); // Adoooobbe

中间有 8 个字母 o,三个三个的被替换后变成两个o,加上剩下的两个 o,所以结果是四个 o


做一个搜索关键词高亮显示效果,

原理就是给第一个参数 param1 加一个高亮样式后返回

正则表达式拼接关键词后是一个字符串,用 RegExp 方式的参数传的是字符串,所以用它定义正则表达式

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>搜索关键词高亮</title>
<style>
</style>
</head>
<body>

<script>
 
var str = '<span style="color:#999">快乐的池塘里面,有一只小青蛙,跳啊跳啊跳</span>';

var pond = '池塘';
var frog = '小青蛙'
var jump = '跳';

var keywords = `(${pond}|${frog}|${jump})`;

var reg = new RegExp(keywords, 'g');

var result = str.replace(reg, function(param1, param2, param3){
  return `<span style="color:#008c8c">${param1}</span>`;
});

document.body.innerHTML = result;

</script>
</body>
</html>


五、练习

使用正则拆分 url

var str = "https://www.baidu.com/s?tn=68018901_3_dg&ie=UTF-8&wd=flash";

var reg = /(http|https):\/\/([\w\.]+):?(\d*)([\/\w\.%-]*)\??([\w=&]+)?/mig;

console.log(reg.exec(str));

/(http|https):\/\/

协议以http或https开头,

斜线 / 在和正则表达式起始符 / 重复了,要用反 \ 斜线转义


/(http|https):\/\/([\w\.]+)/

域名规则是由多个字母、点、数字组成,

正则表达式里面的“点”表示单个字符,除换行和行结束符以外的所有字符,也需要用反斜杠转义

+ 号表示1至多个


/(http|https):\/\/([a-z\.]+):?(\d*)?/ 可以不要最后一个?号

端口号由冒号和多个数字组成,

: 0次1次

\d 0次1次或多次


/(http|https):\/\/([\w\.]+):?(\d*)?([\/\w\.%-]*)

路径可能有英文字符、-、点、中文、斜杠、中文,中文部分转义后有 %,

abc.com/ 域名后面的斜线是路径,有时候默认不显示,所以可以出现0

路径是多个英文字符,也可能没有,所以用量词 *


/(http|https):\/\/([a-z\.]+):?([\d]+)?\/([\/\w\.%-]*)\??([\w=&]+)?/mig

参数部分以 ? 号开头,? 号是量词也需要转义


exec 方法匹配返回的是类数组,

[0] 是整个正则表达式匹配成功的内容

[1] 第一个子表达式匹配的内容,

[2] 第二个子表达式

[3]

... 是从左往右数,如果有嵌套的小括号,从外往里依次的数


2. 给 10000000000 每三个 0 打一个点变成 10.000.000.000


我们想匹配的非单词边界有一个特点,它后面跟着的是三个数字

\d{3} 代表三个连续的数字 \d\d\d

量词里面有一个“正向预查” ?=n

m(?=n) 匹配后面紧跟着 n 的 m,意思是后面的 n 是条件,匹配的是前面的 m

var str = "10000000000";

var reg = /\B(?=\d{3})/;

console.log(str.replace(reg, ".")); // 1.0000000000


全局匹配

var str = "10000000000";

var reg = /\B(?=\d{3})/g;

console.log(str.replace(reg, ".")); // 1.0.0.0.0.0.0.0.000

1.0000000000 第一个非单词边界的位置,后面跟着三个数字

1.0.000000000 第二个非单词边界的位置,后面跟着三个数字

1.0.0.00000000 第三个非单词边界的位置,后面跟着三个数字

1.0.0.0.0000000 依次类推

1.0.0.0.0.000000

1.0.0.0.0.0.00000

1.0.0.0.0.0.0.0000

1.0.0.0.0.0.0.0.000 匹配结果


我们需要的是从后往前,每三位数字打一个点

需要加 $ 符号表示从后往前

现在我们匹配的是,以三个数字结尾的单词边界 10000000.000

var str = "10000000000";

var reg = /\B(?=\d{3}$)/g;

console.log(str.replace(reg, ".")); // 10000000.000


想再往前面接着每三个数字打一个点,就是3的倍数6

var str = "10000000000";

var reg = /\B(?=(\d{3})+$)/g;

console.log(str.replace(reg, ".")); // 10.000.000.000

{3} 是量词,+号也是量词,所以前面的 {3} 要加一个小括号和 + 隔开


(?=..) 都需要加上括号

+? 问号在量词后面的时候不需要加括号,代表非贪婪匹配


6. 统一空格

去掉多余的空格

var str = "Regexp    remove    string spaces";

var reg = /( )+/g;

console.log(str.replace(reg, ' ')); // Regexp remove string spaces


附,面试题

1. 正则表达式实现aabb的形式变成bbaaa

2. 给 10000000000 每三个 0 打一个点变成 10.000.000.000

3. 字符串去重 aaaaaaabbbbcccc 变成 abc

4. 把 the-first-name 转换成小驼峰 theFirstName

5. 匹配结尾的数字

6. 统一空格

7. 判断字符串是不是由数字构成

8. 删除字符串中的空格

9. 身份证号匹配

10. 将字符串

"select student.*, result.* from student inner join result on student.id = result.studentid"

和"select * from student"

中的 student 替换成 key 值



Leave a comment 0 Comments.

Leave a Reply

换一张