美文网首页前端开发
正则-由浅入深

正则-由浅入深

作者: 小程序前端超市 | 来源:发表于2018-11-22 10:49 被阅读101次

前言

本篇多以网上资料参考及个人理解为主,希望能帮助到大家,当然在不足的地方请多多包含。

一、介绍

正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。

二、特点

  1. 灵活性、逻辑性和功能性非常强;
  2. 可以迅速地用极简单的方式达到字符串的复杂控制。

三、元字符与修饰符

1、元字符

\ ^ $ . * + ? \d \D \w \W \s \S {n} {n,} {n, m} 等

2、修饰符

字符 描述
i 不区分大小写
g 全局匹配

四、优先级

运算符 描述
\ 转义符
(), (?:), (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, \任何元字符、任何字符 定位点和序列(即:位置和顺序)
| 替换,"或"操作,字符具有高于替换运算符的优先级,使得"m|food"匹配"m"或"food"。若要匹配"mood"或"food",请使用括号创建子表达式,从而产生"(m|f)ood"。

五、实例方式

1、/pattern/ (比较常用)

/he/.test('hello') // true

2、new RegExp (可传变量)

var char = 'e';  
new RegExp('h'+char).test('hello'); // true

六、常用匹配方法

1、test() 方法用于检测一个字符串是否匹配某个模式。返回true/false

/he/.test('hello') // true

2、match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。返回匹配结果数组或null

a、无匹配结果

var str = 'hello world ~';
var result = str.match(/z/); // null

b、无g修饰符(只输出第一个匹配结果)

var str = 'aaa_aa_a';
var pattern = /a+/;
console.log(str.match(pattern)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]
console.log(str.match(pattern)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]

c、有g修饰符(输出所有匹配结果)

var str = 'hello world ~';
var result = str.match(/o/g); // ["o", "o"]

3、exec() 方法用于检索字符串中的正则表达式的匹配。返回匹配结果数组或null

a、无匹配结果

var str = 'hello world ~';
var result = /z/.exec(str); // null

b、无g修饰符(只输出第一个匹配结果,这点同match一样)

var str = 'aaa_aa_a';
var pattern = /a+/;
console.log(pattern.exec(str)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]
console.log(pattern.exec(str)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]

c、有g修饰符(会记住上一次匹配结果的位置,查找下一个匹配,可以反复调用,直接到没匹配结果)

var str = 'aaa_aa_a';
var pattern = /a+/g;
console.log(pattern.exec(str)) // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]
console.log(pattern.exec(str)) // ["aa", index: 4, input: "aaa_aa_a", groups: undefined]
console.log(pattern.exec(str)) // ["a", index: 7, input: "aaa_aa_a", groups: undefined]
console.log(pattern.exec(str)) // null

4、search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。返回匹配子字符串的第一个索引位置或-1,这点同indexOf方式类似

a、有匹配结果

var str = 'hello world ~';
var result = str.search(/o/g); // 4

b、无匹配结果

var str = 'hello world ~';
var result = str.search(/z/); // -1

5、replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。返回一个替换过的新字符串

var str = 'hello world ~';
str = str.replace(/hello/, 'hi');
console.log(str); // hi world ~

6、split() 方法用于把一个字符串分割成字符串数组。返回一个字符串数组

var str = 'hello world ~';
var arr = str.split(/o/);
console.log(arr); // ["hell", " w", "rld ~"]

7、compile() 方法也可用于改变和重新编译正则表达式。

var str = 'hello World.';
var pattern = /wo/;
console.log(new RegExp(pattern).test(str)); // 结果:false
var pattern2 = pattern.compile(/wo/i);
console.log(new RegExp(pattern).test(str)); // 结果:true

七、获取匹配结果

将( 和 ) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。

1、RegExp.$1 ($2,$3...)

var str = 'hello world ~';
var result = /(he)(l)/.test(str);
console.log(RegExp.$1, RegExp.$2); // he l

2、groups分组起名(ES6)

var pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
var result = pattern.exec('2017-01-25');

也可以解析赋值

var {groups: {one, two}} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
console.log(one, two); // foo bar

八、贪婪模式与惰性模式

1、贪婪模式(默认)

限定符:+, ?, *, {n}, {n,}, {n,m}

var pattern = /8[a-zA-Z0-9]*7/g;
var string = "abc8defghij7klngon8qrstwxy7";
console.log(string.match(pattern)) // ["8defghij7klngon8qrstwxy7"]

2、惰性模式

标识符:+?, ??, *?, {n}?, {n,}?, {n,m}? (在限定符后加?)

var pattern = /8[a-zA-Z0-9]*?7/;
var string = "abc8defghij7klngon8qrstwxy7";
console.log(string.match(pattern)) // ["8defghij7"]

九、获取匹配与非获取匹配

1、获取

规则 描述 示例
(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在JScript中则使用1…9属性。要匹配圆括号字符,请使用“(”或“)”。 RegExp.$1
var result = /(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)/.test('abcdefghijklmn');
console.log(RegExp.$0); // undefined
console.log(RegExp.$1); // a
console.log(RegExp.$9); // i
console.log(RegExp.$10); // undefined

2、非获取

规则 描述
(?:pattern) 非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分时很有用。
/industr(?:y|ies)/.test('industried') // false
/industr(?:y|ies)/.test('industry') // true
console.log(RegExp.$1); // 没有任何内容

上面匹配相当于这样,只不过是把公共部分拿出来了而已

/industry|industries/.test('industried') // false
规则 描述
(?=pattern) 非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。
/Windows(?=95|98|NT|2000)/.test('acbWindowsXPfff') // false
/Windows(?=95|98|NT|2000)/.test('acbWindowsNTfff') // true
规则 描述
(?!pattern) 非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。
/Windows(?!95|98|NT|2000)/.test('abcWindows98') // false
/Windows(?!95|98|NT|2000)/.test('abcWindowsXP') // true
规则 描述
(?<=pattern) 非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。
/(?<=95|98|NT|2000)Windows/.test('abcXPWindows') // false
/(?<=95|98|NT|2000)Windows/.test('abc98Windows') // true
规则 描述
(?<!patte_n) 非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。
/(?<!95|98|NT|2000)Windows/.test('abc2000Windows') // false
/(?<!95|98|NT|2000)Windows/.test('abcXPWindows') // true

3、结合贪婪示例

var b = "abeeee:eeeee:eeeeeab";

console.log(b.match(/e+\:e+/g));            // ["eeee:eeeee"]

console.log(b.match(/e+\:e+?/g));           // ["eeee:e", "eeee:e"]

console.log(b.match(/e+\:(?=e+)/g));        // ["eeee:", "eeeee:"]

console.log(b.match(/e+?\:(?=e+)/g));       // ["eeee:", "eeeee:"]

console.log(b.match(/e+?\:(?!e+)/g));       // null

console.log(b.match(/(?:e+)?\:(?:e+?)/g));  // ["eeee:e", "eeee:e"]

十、开发常用

1、匹配1-5个中文

/^[\u4E00-\u9FA5]{1,5}$/.test('张三'); // true

2、匹配邮箱

/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test('abc@123.com'); // true

3、sublime编辑器中字符串替换

把import引入方式替换成require引入方式

let Home = ()=>import('@/pages/home')
let About = ()=>import('@/pages/about')
let List = ()=>import('@/pages/List')

规则

\(\)=>import\('(.*)'\)
require "$1";

4、匹配数字

var price = 120.36;
console.log(/^\d+\.?\d+$/.test(price)); // true

5、匹配手机号

/^1[3|4|5|7|8][0-9]{9}$/.test(18235661888); // true

6、匹配所有空白行

规则

^\s*\n

7、获取url参数值

假定url为:file:///E:/xampp/htdocs/test/test.html?name=tom&age=24 获取name值

/(?<=name=)\w+/.exec(location.href) // ["tom",...]

8、tab转空格

\t

十一、不常用匹配

1、\cx元字符

匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。

console.log(/\cM/.test("回车符\r")); // true

2、\num

匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。

a、示例:字符串去重

var str = 'a,a,b,b,b,cc';
console.log(str.replace(/(\w,).*\1/g, '$1')); // a,b,cc

上面代码需要连续重复才行,是有条件约束的,不然像下面的代码就不是自己想要的了。

var str = 'a,a,b,b,d,b,cc';
console.log(str.replace(/(\w,).*\1/g, '$1')); // a,b,cc

\1表示重复正则第一个圆括号内匹配到的内容
\2表示重复正则第二个圆括号内匹配到的内容
以此类推

/(h)(e)\1\2/g.test('hehello') // true
/(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)\10/g.test('abcdefghijjklmn') // true

3、\xn匹配16进制字符

匹配n,其中n为十六进制转义值

/\x41/.exec('A') // ["A", index: 0, input: "A", groups: undefined]

'A'.charCodeAt() // 十进制65
'A'.charCodeAt().toString(16) // 十进制65 转十六进制为41

parseInt(41, 16) // 16进制的41 转十进制为65
String.fromCharCode(65) // A

十二、正则模拟js方法

1、indexOf()查找子符开始位置索引

使用search实现

'hello'.indexOf('e'); // 1
'hello'.search(/e/); // 1

2、trim()去除前后空格

使用replace实现

'   hello   '.trim(); // hello
'   hello   '.replace(/^\s+|\s+$/g, ''); // hello

3、isNaN判断是否是一个数字

console.log(isNaN(123));                    // false
console.log(isNaN(-1.23));                  // false
console.log(isNaN(5-2));                    // false
console.log(isNaN(0));                      // false
console.log(isNaN("Hello"));                // true
console.log(isNaN("2005/12/12"));           // true

var pattern = /^\-?\d+[.-]?(\d+)?$/;
console.log(!pattern.test(123));            // false
console.log(!pattern.test(-1.23));          // false
console.log(!pattern.test(5-1.2));          // false
console.log(!pattern.test(0));              // false
console.log(!pattern.test('Hello'));        // true
console.log(!pattern.test('2005/12/12'));   // true

十三、jQuery正则剖析

1、trim去除字符前后空格

var trim = function(string) {
  return (string || '').replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
};

\s:空格
\uFEFF:字节次序标记字符(Byte Order Mark),也就是BOM,它是es5新增的空白符
\xA0:禁止自动换行空白符,相当于html中的&nbsp;

2、匹配parents/prevUntil/prevAll

var pattern = /^(?:parents|prev(?:Until|All))/;

console.log(pattern.exec('parent')); // null
console.log(pattern.exec('parents')); // ["parents", index: 0, input: "parents", groups: undefined]
console.log(pattern.exec('parentsUntil')); // ["parents", index: 0, input: "parentsUntil", groups: undefined]

console.log(pattern.exec('prev')); // null
console.log(pattern.exec('prevUntil')); // ["prevUntil", index: 0, input: "prevUntil", groups: undefined]
console.log(pattern.exec('prevAll')); // ["prevAll", index: 0, input: "prevAll", groups: undefined]

3、匹配标签

var pattern = /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>)$/i;

// ^< 以<开头
// ([a-z][^\/\0>:\x20\t\r\n\f]*) 第一组 两个集合[a-z]、[^\/\0>:\x20\t\r\n\f]及*匹配前面的子表达式任意次 \x20空格
// [\x20\t\r\n\f]*
// \/?> 有没有/都可以,如<b>或<b/>
// (?:<\/\1>|)$  ?:非获取匹配 \1表示前面的组,也就是重复前端的规则,>结尾

console.log(pattern.exec('<b>'));               // ["<b>", "b", index: 0, input: "<b>", groups: undefined]
console.log(pattern.exec('<div/>'));            // ["<div/>", "div", index: 0, input: "<div/>", groups: undefined]
console.log(pattern.exec('<div\0>'));           // null
console.log(pattern.exec('<div>>'));            // null
console.log(pattern.exec('<div:>'));            // null 
console.log(pattern.exec('<div\n>'));            // null 
console.log(pattern.exec('<div></div>'));       // ["<div></div>", "div", index: 0, input: "<div></div>", groups: undefined]
console.log(pattern.exec('<div>hello</div>'));  // null
转义字符 意义 ASCII码值(十进制)
\a 响铃(BEL) 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\v 垂直制表(VT) 011
\\ 代表一个反斜线字符''' 092
\' 代表一个单引号(撇号)字符 039
\" 代表一个双引号字符 034
\? 代表一个问号 063
\0 空字符(NUL) 000
\ddd 1到3位八进制数所代表的任意字符 三位八进制
\xhh 1到2位十六进制所代表的任意字符 二位十六进制

十四、ES6扩展

1、RegExp参数扩展

new RegExp('he', 'i').test('HEllo') // true
new RegExp(/he/i).test('HEllo') // true
new RegExp(/he/i, 'g').test('HEllo') // false 因为前面的修饰符i会被后面的g代替,所以返回false

2、\u修饰符

含义为“Unicode 模式”,用来正确处理大于\uFFFF的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。

/^\uD83D/u.test('\uD83D\uDC2A') // false \uD83D\uDC2A是一个四个字节的 UTF-16 编码,代表一个字符
/^\uD83D/.test('\uD83D\uDC2A') // true ES5 不支持四个字节的 UTF-16 编码,会将其识别为两个字符,所以返回true

a、点字符

对于码点大于0xFFFF的 Unicode 字符,点字符不能识别,必须加上u修饰符。

var str = '𠮷';
/^.$/.test(str) // false
/^.$/u.test(str) // true

b、Unicode 字符表示法

这种表示法在正则表达式中必须加上u修饰符,才能识别当中的大括号,否则会被解读为量词。

/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true

c、i修饰符

有些 Unicode 字符的编码不同,但是字型很相近,比如,\u004B与\u212A都是大写的K。

/[a-z]/i.test('\u212A') // false
/[a-z]/iu.test('\u212A') // true

d、RegExp.prototype.unicode属性

判断是否设置了u修饰符

var pattern = /hello/u;
console.log(pattern.unicode); // true

3、y修饰符

y修饰符,叫做“粘连”(sticky)修饰符。

y修饰符的作用与g修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g修饰符只要剩余位置中存在匹配就可,而y修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。

var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;

console.log(r1.exec(s)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]
console.log(r1.exec(s)); // ["aa", index: 4, input: "aaa_aa_a", groups: undefined]
console.log(r1.exec(s)); // ["a", index: 7, input: "aaa_aa_a", groups: undefined]

console.log(r2.exec(s)); // ["aaa", index: 0, input: "aaa_aa_a", groups: undefined]
console.log(r2.exec(s)); // null

这样改变一个规则,y修饰符就会返回结果了

var s = 'aaa_aa_a';
var r = /a+_/y;

console.log(r.exec(s)); // ["aaa_", index: 0, input: "aaa_aa_a", groups: undefined]
console.log(r.exec(s)); // ["aa_", index: 4, input: "aaa_aa_a", groups: undefined] 
console.log(r.exec(s)); // null 

实例:替换规定条件文本

console.log('aaxa'.replace(/a/gy, '-')); // '--xa'

上面代码中,最后一个a因为不是出现在下一次匹配的头部,所以不会被替换。

a、RegExp.prototype.sticky属性

判断是否设置了y修饰符

var r = /hello\d/y;
console.log(r.sticky); // true

4、RegExp.prototype.flags属性

a、ES5 的 source 属性,返回正则表达式的正文

console.log(/abc/ig.source); // abc

b、flags 属性,返回正则表达式的修饰符

console.log(/abc/ig.flags); // ig

5、断言

a、先行断言(ES5)

”先行断言“指的是,x只有在y前面才匹配,必须写成/x(?=y)/。

console.log(/\d+(?=%)/.exec('abc 100% efg'));  // ["100", index: 4, input: "abc 100% efg", groups: undefined]
console.log(/\d+(?!%)/.exec('abc 44 efg'));    // ["44", index: 4, input: "abc 44 efg", groups: undefined]

b、后行断言(ES6)

“后行断言”正好与“先行断言”相反,x只有在y后面才匹配,必须写成/(?<=y)x/。

console.log(/(?<=%)\d+/.exec('abc %100 efg'));  // ["100", index: 5, input: "abc %100 efg", groups: undefined]

6、Unicode属性类

允许正则表达式匹配符合 Unicode 某种属性的所有字符。

var pattern = /\p{Script=Greek}/u;
console.log(pattern.test('π')); // true

上面代码中,\p{Script=Greek}指定匹配一个希腊文字母,所以匹配π成功。

对于某些属性,可以只写属性名,或者只写属性值。

\p{UnicodePropertyName}
\p{UnicodePropertyValue}

\P{…}是\p{…}的反向匹配,即匹配不满足条件的字符。

7、引用

var pattern = /^(?<word>[a-z]+)!\k<word>$/;
console.log(pattern.test('abc!abc')); // true

或者\1依然有效

var pattern = /^(?<word>[a-z]+)!\1$/;
console.log(pattern.exec('abc!abc'));

8、String.prototype.matchAll

可以一次性取出所有匹配。不过,它返回的是一个遍历器(Iterator),而不是数组。

const string = 'test1test2test3';

// g 修饰符加不加都可以
const regex = /t(e)(st(\d?))/g;

for (const match of string.matchAll(regex)) {
  console.log(match);
}
// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]

参考连接:
ES6正则扩展:http://es6.ruanyifeng.com/#docs/regex
ASCII码对照表:http://ascii.911cha.com/
Unicode码对照表:https://www.cnblogs.com/chris-oil/p/8677309.html

相关文章

  • 正则-由浅入深

    前言 本篇多以网上资料参考及个人理解为主,希望能帮助到大家,当然在不足的地方请多多包含。 一、介绍 正则表达式,又...

  • 100个Java练手项目,献给嗜学如狂的人,没看过还敢骄傲?

    给大家推荐一条由浅入深的JAVA学习路径,首先完成 Java基础、JDK、JDBC、正则表达式等基础实验,然后进阶...

  • 20个Java练手项目,献给嗜学如狂的人

    给大家推荐一条由浅入深的JAVA学习路径,首先完成 Java基础、JDK、JDBC、正则表达式等基础实验,然后进阶...

  • 由浅入深

    所谓“由浅入深”,其实是我近来画画的一个感悟——先上浅色,再叠加深色。同时伴随着修饰形态和材质。这个道理对于很多绘...

  • 事半功倍——正则表达式由浅入深 及 iOS实战

    零. 前言 在前面写的三篇文章:iOS分类 同名方法自动检测脚本、iOS瘦身——移除无用资源的LSUnusedRe...

  • Block

    参考文献 Block本质Block由浅入深(5):三种类型的BlockBlock由浅入深(3):Block捕获局部...

  • 由浅入深Runtime

    了解OC方法的调用 OC方法的调用本质是消息机制。任何方法的调用都会转成运行时的 第一个必须参数:消息接受者;第二...

  • 线程:由浅入深

    线程 操作系统在分配资源时候是把资源分配给进程的,但是 CPU 资源就比较特殊,它是分派到线程的,因为真正要占用 ...

  • Block 由浅入深

    Block是对象。 如何判断mrc,arc 1.delloc 能否调用super,能mrc,不能arc。 2.re...

  • grunt由浅入深

    Grunt是一个任务管理器,能大大提高您运行前端开发工作流程。使用大量的Grunt插件可以自动执行任务,例如编译S...

网友评论

    本文标题:正则-由浅入深

    本文链接:https://www.haomeiwen.com/subject/iqukqqtx.html