正则表达式( regular expression )
- RegExp
- 用处: 匹配, 验证; 替换, 截取, 分析
元字符
#### 基本元字符:
匹配任意的非换行字符, 常常表示通用元字符使用 [\s\S]
匹配一个出现在 [] 中的字符, [123456789123456789123456789], [a-z0-9A-Z]
计算机只认识 数字, 所以我们写的字符串计算机其实不是以字符串的形式存储
有一个编码系统: unicode
'0' 48
'1' 49
...
'9' 57
写上: '012' , 在内存中存储: 48 49 50
需要记住的是:
'0' 48
'a' 97
'A' 65
\u0030
() 1> 分组; 2> 提升优先级
| 或者
匹配 foot 或者 food
foot|food
注意: | 具有最低优先级
^(foot|food)$
- 常见的用法
例如要匹配jq的属性选择器:
[name]
[name=value]
用正则: \[\w+=\w+\]|\[\w+\]
jq 的写法: \[\w+(=\w+|)\]
(foot|food) 匹配: 'foot' 或者 'food'
(foot|) 匹配: 'foot' 或者 ''
food(=food|) 匹配: 'food=food' 或者 'food'
\w+(=\w+|) 匹配: '字符=字符' 或 '字符'
\[\w+(=\w+|)\] 匹配: '[xxx]' 或 '[xxx=vvv]'
\[\s*(\w+)\s*(?:([|!^$~]?=)\s*(\w+)|)\s*\]
=>
\[ \s* (\w+) \s* (?: ([|!^$~]?=) \s* (\w+) |) \s* \]
#### 限定元字符
+ (abc)+ {1,}
* {0,}
?
{n}
{n,}
{n,m}
#### 首尾元字符
^ 必须以 xxx 开头
表示否定:
[abc] 用来匹配 一个 字符 a, 或 b, 或 c
[^abc] 用来匹配一个字符, 要求不允许是 a, b, c
$ 必须以 xxx 结尾
表示组引用
a(b)c
#### 简写元字符
\w word
\W
\s space
\S
\d digit
\D
#### 其他:
(?: )
\1
基本的匹配案例
如何匹配
1) 匹配任意的自然数, 就是 0, 1, 2, 3, ...
[0-9] 等价于 \d
分类讨论
-> 一位数字: \d
-> 两位: [1-9]\d
-> 任意位( 除了一位 ): [1-9]\d+
合并:
\d|[1-9]\d+
可以进行演变( 用不同的方法写同一个例子 )
[1-9]\d*|0
[1-9][0-9]*|0
...
2) 匹配任意的整数, 0, +1, -1, +2, -2, ...
[+-]?[1-9][0-9]*|0
(+|-|)[1-9][0-9]*|0
...
3) 匹配任意的实数, 0.01, 0.0000123, -0.1234567
整数 + 小数部分
分类( 划归 )
((+|-|)[1-9][0-9]*|0)(\.\d+|)
无法匹配 -0.1
无非就是不允许匹配 -0.0+
使用正则的原则: 够用即可, 尽可能写成多步的判断, 要保证正则表达式的尽可能简洁
-0+(\.|)0*
if ( !... ) {
if ( ... )
}
\d{17}|[\dxX]
\d{11}
4) 匹配邮箱
[^@]+@[^@]+
如果想要严谨一点
邮箱分成用户名 和 域名
用户名: 可以有数字, 字母下划线组成( 还有一些特殊字符 )
[\w\-]+
域名: xxx.com.cn.xxxx
[\w\-]+(\.[\w\-]+)+
5) 匹配 ip 地址
采用数字点分的方法描述, 而且每一个数字的取值在 0-255 之间, 都可以取到
0.0.0.0
255.255.255.255
123.124.125.126
...
\d{1,3}(\.\d{1,3}){3} 或者: \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
利用正则实现匹配 0 到 255 的数字
分类:
一位: [0-9]
两位: 10-99
[1-9][0-9]
三位: 100-255
开始为 1 的: 100-199: 1[0-9][0-9]
开始为 2 的: 200-255:
中间为 0-4 的: 2[0-4][0-9]
中间为 5 的: 25[0-5]
[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]
IP:
[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]\.[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]\.[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]\.[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]
- 如配匹配计算机中的一个文件路径全名
例如: C:\myMusics\list\myFavorite\rain.MP3
盘符: 一个字符: a-z, 简单的写 . 如果想要严格处理 [a-z]
冒号和斜线: :\\
路径(文件夹的名字): .+\\ 如果想要精确: ([^|:\?\*><\\\/\"]+\\)*
文件名: .+\..+ 如果想要精确一点: [^|:\?\*><\\\/\"]+\.[^|:\?\*><\\\/\"]+
组合起来:
.:.*\\.+\..+
注意: 正则表达式有贪婪模式
C:\myMusics\list\myFavorite\rain.MP3
.:(.*)\\.+\..+
.:.*\\.+.+
如果一个正则表达式中有多个 .+ 或 .* 的结构, 如果造成了匹配的歧义, 满足一个规则. 即贪婪模式
从左往右 匹配个数的能力是左强又弱, 而且只有两个级别, 要么强, 要么弱
例如:
var s = '1234567890';
// 1 2 3
var r = /(.+)(.+)(.+)/;
var res = r.exec( s );
例如匹配 div 标签
var r = /<div.*?>.*<\/div>/;
如果不希望有贪婪模式在 限定元字符( +, * 等 )后写上 ?
字符串提取( 解析 )
.exec 方法
利用正则表达式, 去处理字符串, 并将字符串中符合正则表达式的数据取出来
var s = '1234567';
var r = /\d/;
var m = r.exec( s ); // m 是 match 的简写, 表示捕获
// 如果匹配上了, 就返回数组, 第 0 项 就是匹配到的结果, 如果正则表达式中有 分组
// 分组都是从 1 开始的编号, 那么对应的 第 n 组匹配到的结果就在这个数组的第 n 项
// 注意: 是真数组
// 如果没有匹配到 则返回 null
分组: 就是 ()
从左往右数 (, 第一个开始编号, 从 1 开始编号, 此时编号完成以后, 对应圆括号组成的就是对应的组
// 1 2 34 5
例如: r = /(\d)(\d((\d)\d))(\d)/;
例子:获取网页中的QQ号:
想要做一个推广, 在论坛中发布我有XXX的视频, 如果想要的留下邮箱
如果有人留下的是 QQ 号, 将其拉到一个群里
注意: exec 匹配提取, 只能匹配提取第一个被捕获的数据
如果需要匹配字符串中所有符合要求的字符串
1) 开启全局模式, 在正则后面写上一个 g, 或在构造函数的 第二个参数中提供 'g'
创建正则:
-> 字面量: /.+\\.+/g
-> 构造函数: new RegExp( '.+\\\\.+', 'g' )
2) 开启全局模式后, 使用 正则表达式对象 调用 exec 方法一次, 就会从 0 位置或上一次结束的位置开始
查找下一个匹配结果, 如果查找到结果则返回对应数组, 如果没有查找到( 即, 找完了 )就会返回 null.
如果还继续调用 exec, 那么就从头开始再查一次.
例如:
var s = '123';
var r = /\d/g;
r.exec( s ); // [ '1' ]
r.exec( s ); // [ '2' ]
r.exec( s ); // [ '3' ]
r.exec( s ); // null
r.exec( s ); // [ '1' ]
...
一般要取到所有的数据, 可以使用下面的代码结构
var m;
while ( ( m = r.exec( s ) ) != null ) {
// 此时 m 就是找到的结果
}
字符串替换
.replace( 正则表达式, 需要替换的字符串 )
1) 将一个字符串中所有的 - 换成一个连线
var s = 'a-----------------------------------b----------------------c----d';
// => a-b-c-d
var res = s.replace( /-+/g, '-' );
应用背景
/\*.*?([\r\n]+.*?)*\*/
换行: \r\n
因此 \r 是回车的意思
\n 是换行的意思
平时在处理换行的时候要注意: 在 有些时候是 \r\n, 有些时候是 \n
在 类 Unix 操作系统中( unix, linux, mac ) 换行都是 \n
在 windows 中是 换行是 \r\n
2) 在替换中还可以使用组
var s = '2012-3-4';
// 在不同的系统中想要显示成
// 2012年3月4日
// 3月4日2012年
// 4/3/2012
// ...
var r = /(\d+)\-(\d+)\-(\d+)/;
var res = s.replace( r, '$1年$2月$3日' )
3) 替换的时候, 第二参数可以是函数, 函数的参数为, 匹配到的数组的散列值
利用函数的返回值替换匹配到的结果
var s = '2012-3-4';
var r = /(\d+)\-(\d+)\-(\d+)/;
s.replace( r, function ( match, g1, g2, g3 ) {
return g1 + '年' + g2 + '月' + g3 + '日';
});
电话号码的加密
var list = [
'12345678901',
'12345678912',
'12345678923',
'12345678934',
'12345678945',
'12345678956'
];
var res = list.map( function ( v ) {
// 电话号码的前4位
// 电话号码的后 2 位
return v.replace( /(\d{4})(\d+)(\d{2})/, function ( a, b, c, d ) {
// a 就是匹配到的 电话号码
// b 就是匹配到的 电话号码的前 4 位
// c 就是匹配到的电话号码的 中间 5 位
// d 就是匹配到的电话号码的 后 2 位
return b + '***********'.slice( 0, c.length ) + d;
// return b + '*****' + d;
});
});
(?:)
var s = '1234567';
// 1 2 3 4
var r1 = /(\d)(\d\d\d)(\d\d)(\d)/;
r1.exec( s ); // => [ '1234567', '1', '234', '56', '7' ]
\
// 1 2 3
var r2 = /(\d)(?:\d\d\d)(\d\d)(\d)/;
r2.exec( s ); // => [ '1234567', '1', '56', '7' ]> 这里输入引用文本
希望对大家有帮助O(∩_∩)O~~