跳转至

一些正则

1. 断言(环视)

1.1. 正向先行断言

正向先行断言:(?=表达式),指在某个位置往右看,所在的位置右侧必须匹配表达式。

reg = /喜欢(?=你)/

'他很喜欢你'.match(reg)[0]
// 喜欢


'喜欢你'.match(reg)[0]
// 喜欢


'喜欢他'.match(reg)
// null

'喜欢跑步'.match(reg)
// null

1.2. 反向先行断言

反向先行断言:(?!表达式),指在某个位置往右看,不能存在表达式中的内容。

reg = /喜欢(?!你)/

'她喜欢你'.match(reg)
// null

'她喜欢你的微笑'.match(reg)
// null

'她喜欢篮球'.match(reg)[0]
// 喜欢

'喜欢做饭'.match(reg)
// 喜欢

1.3. 正向后行断言

正向后行断言:(?<=表达式),指在某个位置往左看,存在表达式中的内容。

reg = /(?<=我)喜欢/

'他喜欢你'.match(reg)
// null

'喜欢做饭'.match(reg)
// null

'喜欢你的微笑'.match(reg)
// null

'我喜欢篮球'.match(reg)
// 喜欢

1.4. 反向后行断言

反向后行断言:(?<!表达式),指在某个位置往左看,不能存在表达式中的内容。

reg = /(?<!我)喜欢/

'他喜欢你'.match(reg)
// 喜欢

'喜欢做饭'.match(reg)
// 喜欢

'喜欢你的微笑'.match(reg)
// 喜欢

'我喜欢篮球'.match(reg)
// null

2. 正则表达式中匹配一个反斜杠要用四个反斜杠,为什么呢?

console.log(new RegExp('\\\\').test('a\\1'))
// true

分析一下 \\\\,第一个斜杠是转义符,第二个斜杠是斜杠本身,第三个斜杠是转义符,第四个斜杠是斜杠本身。

有 2 点要清楚:

  1. 字符串里面表示斜杠就需要两个斜杠如 \\

  2. 正则表达式里的斜杠需要转义,是用 \\ 标示。

这样就比较好解释:

我们先要表示正则表达式里面的斜杠 \\,然后再用字符串表示出来。而这 2 个斜杠分别需要一个转义符,这样就成了 4 个斜杠在正则表达式里面表示一个斜杠。

3. 几个正则

  • '^$'头尾相连,表示空行
  • \w匹配字类字符,包括下划线,等于 [0-9a-zA-Z_]
  • \b匹配单词的分隔,grep ''\bx\b' passwd

4. 正则中的()[]有本质的区别

  • ()内的内容表示的是一个子表达式,()本身不匹配任何东西,也不限制匹配任何东西,只是把括号内的内容作为同一个表达式来处理,例如(ab){1,3},就表示ab一起连续出现最少1次,最多3次。 如果没有括号的话,ab{1,3},就表示a,后面紧跟的b出现最少1次,最多3次。 另外,括号在匹配模式中也很重要。
  • []表示匹配的字符在[]中,并且只能出现一次,并且特殊字符写在[]会被当成普通字符来匹配。例如[(a)],会匹配(、a、)、这三个字符。

所以() [] 无论是作用还是表示的含义,都有天壤之别,没什么联系

4.1. 圆括号()是组,主要应用在限制多选结构的范围/分组/捕获文本/环视/特殊模式处理

示例:

  1. (abc|bcd|cde),表示这一段是abc、bcd、cde三者之一均可,顺序也必须一致
  2. (abc)?,表示这一组要么一起出现,要么不出现,出现则按此组内的顺序出现
  3. (?:abc)表示找到这样abc这样一组,但不记录,不保存到$变量中,否则可以通过$x取第几个括号所匹配到的项,比如:(aaa)(bbb)(ccc)(?:ddd)(eee),可以用$1获取(aaa)匹配到的内容,而$3则获取到了(ccc)匹配到的内容,而$4则获取的是由(eee)匹配到的内容,因为前一对括号没有保存变量
  4. a(?=bbb) 顺序环视 表示a后面必须紧跟3个连续的b
  5. (?i:xxxx) 不区分大小写 (?s:.*) 跨行匹配.可以匹配回车符

4.2. 方括号是单个匹配,字符集/排除字符集/命名字符集

示例:

  1. [0-3],表示找到这一个位置上的字符只能是0到3这四个数字,与(abc|bcd|cde)的作用比较类似,但圆括号可以匹配多个连续的字符,而一对方括号只能匹配单个字符
  2. [^0-3],表示找到这一个位置上的字符只能是除了0到3之外的所有字符
  3. [:digit:]:0-9,[:alnum:] :A-Za-z0-9

4.3. []注意事项

  • 中括号的-有特殊意义,表示区间
  • [.]中括号的点失去通配符的意义,变成了一个纯字符串

5. o|cb

o|cb出现o或者cb,不是o或者c、然后后面加b。可以使用(o|c)b

1
2
3
4
5
console.log(/^(o|cb)$/.test('ob'))
// false

console.log(/^(o|c)b$/.test('ob'))
// true

6. 常见正则

6.1. MAC和IP地址正则匹配

  1. MAC地址正则匹配:
    ([A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2}
    
  2. IP地址正则匹配:
    ((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}
    
    其中:

2(5[0-5]|[0-4]\d) 匹配:200 ~ 255 [0-1]?\d{1,2} 匹配:0 ~ 199

6.2. 正则匹配两位以内有效数字

const pattern = /^\d+([.]{1}(\d){0,2}){0,1}$/
1
2
3
4
5
6
7
8
9
function getCookie(key) {
  // 从cookie里面取
  const reg = new RegExp(key + "=([^;]*)");
  const m = document.cookie.match(reg);
  if (m && m.length > 1) {
    return m[1];
  }
  return null;
}

6.4. 正则表达式获取URL参数

使用到的正则表达式:

[^\?&]?参数名=[^&]+
1
2
3
4
5
6
7
8
function getURLParam(search, name) {
  var reg = new RegExp("[^?&]?" + encodeURI(name) + "=([^&]+)");
  const arr = search.match(reg);
  if (arr && arr[1]) {
    return arr[1];
  }
  return "";
}

还可以直接用API:

new URLSearchParams()

6.5. 判断是否由重复子串构成,比如abcabcabab

1
2
3
4
function solution(str) {
  const reg = /^(\w+)\1+$/
  return reg.test(str)
}

6.6. 密码强度校验

至少有一个大写字母。至少有一个小写字母。至少有一个数字。至少有 8 个字符。

reg = /(?=.*?[0-9])(?=.*?[a-z])(?=.*?[A-Z])^.{8,}$/

reg.test('Password1')
// true

reg.test('Password')
// false

reg.test('password1')
// false

reg.test('123123123')
// false

6.7. 复杂一点的密码校验

密码长度6-12位,由数字、小写字符和大写字母组成,但必须至少包括2种字符。

6.7.1. 第1种解法

可以把原题变成下列几种情况之一:

  • 同时包含数字和小写字母
  • 同时包含数字和大写字母
  • 同时包含小写字母和大写字母
  • 同时包含数字、小写字母和大写字母

以上的4种情况是或的关系(实际上,可以不用第4条)。

1
2
3
4
5
6
7
var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9A-Za-z]{6,12}$/;
console.log( reg.test("1234567") ); // false 全是数字
console.log( reg.test("abcdef") ); // false 全是小写字母
console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
console.log( reg.test("ab23C") ); // false 不足6位
console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
console.log( reg.test("abcdEF234") ); // true 三者都有

6.7.2. 另外一种解法

“至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。

那么要求“不能全部都是数字”,怎么做呢?(?!p)出马!

对应的正则是:

var reg = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/;

三种“都不能”呢?

最终答案是:

1
2
3
4
5
6
7
var reg = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/;
console.log( reg.test("1234567") ); // false 全是数字
console.log( reg.test("abcdef") ); // false 全是小写字母
console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
console.log( reg.test("ab23C") ); // false 不足6位
console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
console.log( reg.test("abcdEF234") ); // true 三者都有

6.8. 不匹配任何东西的正则

reg = /.^/

因为此正则要求只有一个字符,但该字符后面是开头。

6.9. 数字的千位分隔符表示法

1
2
3
4
reg = /(?!^)(?=(\d{3})+$)/g

'123123123'.replace(reg, ',')
// 123,123,123

首先整个正则只是匹配了位置,而不是字符,所以replace方法并没有替换任何字符,只是在相应位置插入了分隔符。

(?!^)表明这个位置不是开头,也就是避免了,123,123情况。

(?=\d{3}$)可以匹配最后一个分隔符,加上+号可以匹配多个,即(?=(\d{3})+$)

如果要支持处理包含空格的数字,比如123123 123123,则需要把正则中^$替换成\b,即字符边界,正则即为/(?!\b)(?=(\d{3})+)\b/,也就是/(\B)(?=(\d{3})+\b)/

1
2
3
4
reg = /(\B)(?=(\d{3})+\b)/g

'123123 123123'.replace(reg, ',')
// 123,123 123,123

6.10. 保留两位小数的价格输入框

1
2
3
4
5
6
7
8
9
// 输入限制
const changePiece = (e) =>{
      e.target.value = e.target.value.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g, '$1');
  }
  return (
    <div>
       <input type="text" onKeyUp={ (e) => {changePiece(e)}} />
    </div>
  );