看了掘金的一个作者写的JS正则表达式完整教程 受益匪浅,感谢作者的无私奉献。在此,做下笔记。

目录

1. 正则表达式字符匹配

1.1 字符组

[ab]匹配a或者b其中一个字符

1.1.1 常见的简写

\d: [0-9]。表示是一位数字。记忆方式:其英文是digit(数字)。

\D: [^0-9]。表示除数字外的任意字符。

\w: [0-9a-zA-Z_]。表示数字、大小写字母和下划线。记忆方式:w是word的简写,也称单词字符。

\W: [^0-9a-zA-Z_]。非单词字符。

\s: [ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s是space character的首字母。

\S: [^ \t\v\n\r\f]。 非空白符。

.: [^\n\r\u2028\u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。记忆方式:想想省略号...中的每个点,都可以理解成占位符,表示任何类似的东西。

[\d\D]、[\w\W]、[\s\S]和[^] : 任意字符

1.1.2 排除字符组

[^abc] 一个除"a"、"b"、"c"之外的任意一个字符,^(脱字符),表示求反

1.2 量词

1.2.1 简写

{m,n} : 表示至少出现m次,至多出现n次。

{m,} : 表示至少出现m次。

{m} : 等价于{m,m},表示出现m次。

? : 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?

+: 等价于{1,},表示出现至少一次。记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。

*: 等价于{0,},表示出现任意次,有可能不出现。记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。

1.2.2 贪婪匹配
var regex = /\d{2,5}/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["123", "1234", "12345", "12345"]

/\d{2,5}/,表示数字连续出现2到5次。会匹配2位、3位、4位、5位连续数字,你有几位我就匹配几位,能匹配多的绝对不给你匹配少的

有时贪婪不是一件好事,而惰性匹配,就是尽可能少的匹配:

1.2.3 惰性匹配
var regex = /\d{2,5}?/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["12", "12", "34", "12", "34", "12", "34", "56"]

通过在量词后面加个问号就能实现惰性匹配,因此所有惰性匹配情形如下:

{m,n}?

{m,}?

??

+?

*?

1.3 多选分支 |

(p1|p2|p3),其中p1、p2和p3是子模式,用|(管道符)分隔,表示其中任何之一。

var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["good"] var regex = /goodbye|good/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["goodbye"]

也就是说,分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。

1.4 案例分析

1.4.1匹配时间 23:59 02:07

分析:

共4位数字,第一位数字可以为[0-2]。

当第1位为2时,第2位可以为[0-3],其他情况时,第2位为[0-9]。

第3位数字为[0-5],第4位为[0-9]

var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;
1.4.2匹配日期 yyyy-mm-dd

分析:

年,四位数字即可,可用[0-9]{4}。

月,共12个月,分两种情况01、02、……、09和10、11、12,可用(0[1-9]|1[0-2])。

日,最大31天,可用(0[1-9]|[12][0-9]|3[01])。

var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
console.log( regex.test("2017-06-10") );
// => true
1.4.3 window操作系统文件路径

案例 F:\study\javascript\regex\regular expression.pdf

分析:

整体模式是: 盘符:\文件夹\文件夹\文件夹<br/>

盘符:\:[a-zA-Z]+\ 盘符不能为空,\ 要转义

文件夹\:中间含有0或者多个文件夹的层级 文件名或者文件夹名,不能包含一些特殊字符,此时我们需要排除字符组[^\:<>|"?\r\n/]来表示合法字符,另外不能为空名,至少有一个字符,也就是要使用量词+ : [^\:<>|"?\r\n/+]*

最后一部分文件夹没有 \:[^\:*<>|"?\r\n/+]?

var regex = /^[a-zA-Z]:\\([^\\:*<>|"?\r\n/]+\\)*([^\\:*<>|"?\r\n/]+)?$/;
console.log( regex.test("F:\\study\\javascript\\regex\\regular expression.pdf") );
1.4.4 匹配id

提取出id="container"。
```
var regex = /id=".*"/
var string = '

';
console.log(string.match(regex)[0]);
// => id="container" class="main"
```
.是通配符,本身就匹配双引号的,而量词*又是贪婪的,当遇到container后面双引号时,不会停下来,会继续匹配,直到遇到最后一个双引号为止。
解决之道,可以使用惰性匹配:
```
var regex = /id=".*?"/
var string = '

';
console.log(string.match(regex)[0]);
// => id="container"

//优化

var regex = /id="[^"]*"/

var string = '

';

console.log(string.match(regex)[0]);

// => id="container"


##### 1.4.4 匹配带86的手机号 <br/>
13885821234 +8613885821234 08613885821234 (+86)13885821234 (086)13885821234

var string="13885821234 +8613885821234 08613885821234 (+86)13885821234 (086)13885821234"

var reg=/(+86|086|(+86)|(\086))?(1[34578][0-9]{9})/g

string.match(reg)

//["13885821234", "+8613885821234", "08613885821234", "(+86)13885821234", "13885821234"]


#### 2. <span id = "title-2">正则表达式位置匹配</span><br/>
#### 2.1 <span id = "title-2-1">什么是位置呢?</span><br/>
![](http://images2017.cnblogs.com/blog/1181900/201711/1181900-20171103144747763-1652231640.png) #### 2.2 <span id = "title-2-2">如何匹配位置呢?</span><br/>
在ES5中,共有6个锚字符:<br/>
^ $ \b \B (?=p) (?!p)
#####2.2.1 ^和$ <br/>
^(脱字符)匹配开头,在多行匹配中匹配行开头。<br/>
$(美元符号)匹配结尾,在多行匹配中匹配行结尾。<br/>

var result = "hello".replace(/^|$/g, '#');

console.log(result);

// => "#hello#"

多行匹配

var result = "I\nlove\njavascript".replace(/^|$/gm, '#');

console.log(result);

/*

I#

love#

javascript#

*/


#####2.2.2 \b和\B <br/>
\w是字母数字或者下划线的中任何一个字符<br/>
\b是单词边界,具体就是\w和\W之间的位置,也包括\w和^之间的位置,也包括\w和$之间的位置。 <br/>

var result = "[JS] Lesson_01.mp4".replace(/\b/g, '#');

console.log(result);

// => "[#JS#] #Lesson_01#.#mp4#"

\B就是\b的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉\b,剩下的都是\B的。 <br/>

var result = "[JS] Lesson_01.mp4".replace(/\B/g, '#');

console.log(result);

// => "#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4"

#####2.2.3 (?=p)和(?!p) <br/>
(?=p),其中p是一个子模式,即p前面的位置 <br/>
(?=p),一般都理解成:要求接下来的字符与p匹配,但不能包括p的那些字符。<br/>
而在本人看来(?=p)就与^一样好理解,就是p前面的那个位置<br/>
(?!p),除了p前面的所有位置 <br/>

var result = "hello".replace(/(?=l)/g, '#');

console.log(result);

// => "he#l#lo"

var result = "hello".replace(/(?!l)/g, '#');

console.log(result);

// => "#h#ell#o#"


#### 2.3 <span id = "title-2-3">位置的特性</span><br/>
字符之间的位置,可以写成多个。<br/>
把位置理解空字符,是对位置非常有效的理解方式。<br/>

"hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "o" + "";

//true

"hello" == "" + "" + "hello"

//true

var result = /^^hello$$$/.test("hello");

console.log(result);

// => true

var result = /(?=he)^^he(?=\w)llo$\b\b$/.test("hello");

console.log(result);

// => true

#### 2.4. <span id = "title-2-4">相关案例</span><br/>
##### 2.4.1 不匹配任何东西的正则<br/>
/.^/ 此正则要求只有一个字符,但该字符后面是开头 <br/> ##### 2.4.2 数字的千位分隔符表示法<br/>
把"12345678",变成"12,345,678"<br/>
弄出最后一个逗号

var result = "12345678".replace(/(?=\d{3}$)/g, ',')

console.log(result);

// => "12345,678"

弄出所有逗号

var result = "12345678".replace(/(?=(\d{3})+$)/g, ',')

console.log(result);

// => "12345,678"

验证发现有问题

var result = "123456789".replace(/(?=(\d{3})+$)/g, ',')

console.log(result);

// => ",123,456,789"

怎么解决呢?我们要求匹配的到这个位置不能是开头。<br/>
我们知道匹配开头可以使用^,但要求这个位置不是开头怎么办?<br/>
easy,(?!^),你想到了吗?测试如下:<br/>

var string1 = "12345678",

string2 = "123456789";

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

var result = string1.replace(reg, ',')

console.log(result);

// => "12,345,678"

result = string2.replace(reg, ',');

console.log(result);

// => "123,456,789"

把"12345678 123456789"替换成"12,345,678 123,456,789"。 <br/>
此时我们需要修改正则,把里面的开头^和结尾$,替换成\b: <br/>

var string = "12345678 123456789",

reg = /(?!\b)(?=(\d{3})+\b)/g;

var result = string.replace(reg, ',')

console.log(result);

// => "12,345,678 123,456,789"

其中(?!\b)怎么理解呢? <br/>
要求当前是一个位置,但不是\b前面的位置,其实(?!\b)说的就是\B。 <br/>
因此最终正则变成了:/\B(?=(\d{3})+\b)/g。 <br/> ##### 2.4.3 验证密码的问题<br/>
密码长度6-12位,由数字、小写字符和大写字母组成,但必须至少包括2种字符<br/>
不考虑“但必须至少包括2种字符”这一条件<br/>

var reg = /[1]{6,12}$/;

必须包含数字(?=.*[0-9]) <br/>
/(?=.*[0-9])^[0-9A-Za-z]{6,12}$/<br/>
对于这个正则,我们只需要弄明白(?=.*[0-9])^即可。<br/>
分开来看就是(?=.*[0-9])和^。<br/>
表示开头前面还有个位置(当然也是开头,即同一个位置,想想之前的空字符类比)。<br/>
(?=.*[0-9])表示该位置后面的字符匹配.*[0-9],即,有任何多个任意字符,后面再跟个数字。<br/>

var reg = /(?=.*[0-9])[2]{6,12}$/;

同时包含具体两种字符(?=.*[0-9])(?=.*[a-z]) <br/>

var reg = /(?=.[0-9])(?=.[a-z])[3]{6,12}$/;

我们可以把原题变成下列几种情况之一:<br/>

1. 同时包含数字和小写字母
2. 同时包含数字和大写字母
3. 同时包含小写字母和大写字母
4. 同时包含数字、小写字母和大写字母</br/>

var reg = /((?=.[0-9])(?=.[a-z])|(?=.[0-9])(?=.[A-Z])|(?=.[a-z])(?=.[A-Z]))[4]{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 三者都有

解法二
“至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。</br/>
那么要求“不能全部都是数字”,怎么做呢?(?!p)出马! <br/>
var reg = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/;

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 三者都有

#### 3.  <span id = "title-3">正则表达式括号的作用</span><br/>
#### 3.1. <span id = "title-3-1">提取数据</span><br/> 以日期为例。假设格式是yyyy-mm-dd的<br/>

var regex = /(\d{4})-(\d{2})-(\d{2})/;

var string = "2017-06-12";

console.log( string.match(regex) );

// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]

构造函数的全局属性$1-$9,如RegExp.$1匹配结果的第一个引用()内容<br/>

var regex = /(\d{4})-(\d{2})-(\d{2})/;

var string = "2017-06-12";

console.log(RegExp.$1); // "2017"

console.log(RegExp.$2); // "06"

console.log(RegExp.$3); // "12"

#### 3.2 <span id = "title-3-2">替换</span><br/>
把yyyy-mm-dd格式,替换成mm/dd/yyyy怎么做?<br/>

var regex = /(\d{4})-(\d{2})-(\d{2})/;

var string = "2017-06-12";

var result = string.replace(regex, "$2/$3/$1");

console.log(result);

// => "06/12/2017"

//等价于

var regex = /(\d{4})-(\d{2})-(\d{2})/;

var string = "2017-06-12";

var result = string.replace(regex, function() {

return RegExp.$2 + "/" + RegExp.$3 + "/" + RegExp.$1;

});

console.log(result);

// => "06/12/2017"


#### 3.3 <span id = "title-3-3">反向引用</span><br/>
>
2016-06-12
2016/06/12
2016.06.12
>

//缺点:但也匹配"2016-06/12"这样的数据。

var regex = /\d{4}(-|/|.)\d{2}(-|/|.)\d{2}/;

//使用反向引用

var regex = /\d{4}(-|/|.)\d{2}\1\d{2}/;

\1,表示的引用之前的那个分组(-|\/|\.)。不管它匹配到什么(比如-),\1都匹配那个同样的具体某个字符。\2和\3同理<br/>
#### 3.4 <span id = "title-3-4">非捕获分组</span><br/>
只想要括号最原始的功能,但不会引用它,此时可以使用非捕获分组(?:p)<br/>

var regex = /(?:ab)+/g;

var string = "ababa abbb ababab";

console.log( string.match(regex) );

// => ["abab", "ab", "ababab"]


#### 3.5 <span id = "title-3-5">相关案例</span><br/>
##### 3.5.1 trim()方法

//法一

function tirm(str){

return str.replace(/^\s+|\s+$/g,"")

}

//法二 这里使用了惰性匹配?,不然也会匹配最后一个空格之前的所有空格的

function tirm(str){

return str.replace(/^\s
(.?)\s$/g,"$1")

}

##### 3.5.2  将每个单词的首字母转换为大写
replace第一个参数是正则,第二个是字符串或者function,参数第一个是匹配正则的项,第二个是()引用返回的项,以此类推

function titleize(str) {

return str.toLowerCase().replace(/(?:^|\s)\w/g, function(c) {

return c.toUpperCase();

});

}

console.log( titleize('my name is epeli') );

// => "My Name Is Epeli"

##### 3.5.3  驼峰化 <br/>
-moz-transform MozTransform <br/>

function camelize(str) {

return str.replace(/-_\s?/g,function(match,c){

return c?c.toUpperCase():''

})

}

console.log( camelize('-moz-transform') );

//中划线划

function dasherize(str) {

return str.replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase();

}

console.log( dasherize('MozTransform') );

// => "-moz-transform"

单词的界定是,前面的字符可以是多个连字符、下划线以及空白符。正则后面的?的目的,是为了应对str尾部的字符可能不是单词字符,比如str是'-moz-transform    '。<br/>
##### 3.5.4 html转义和反转义化 <br/>

// 将HTML特殊字符转换成等值的实体

function escapeHTML(str) {

var escapeChars = {

'¢' : 'cent',

'£' : 'pound',

'¥' : 'yen',

'€': 'euro',

'©' :'copy',

'®' : 'reg',

'<' : 'lt',

'>' : 'gt',

'"' : 'quot',

'&' : 'amp',

''' : '#39'

};

return str.replace(new RegExp('[' + Object.keys(escapeChars).join('') +']', 'g'), function(match) {

return '&' + escapeChars[match] + ';';

});

}

console.log( escapeHTML('

Blah blah blah

') );

// => "<div>Blah blah blah</div&gt";


// 实体字符转换为等值的HTML。

function unescapeHTML(str) {

var htmlEntities = {

nbsp: ' ',

cent: '¢',

pound: '£',

yen: '¥',

euro: '€',

copy: '©',

reg: '®',

lt: '<',

gt: '>',

quot: '"',

amp: '&',

apos: '''

};

return str.replace(/&([^;]+);/g, function(match, key) {

if (key in htmlEntities) {

return htmlEntities[key];

}

return match;

});

}

console.log( unescapeHTML('<div>Blah blah blah</div>') );

// => "

Blah blah blah

"


##### 3.5.5 匹配成对html标签 <br/>
匹配一个开标签,可以使用正则<[^>]+>,<br/>
匹配一个闭标签,可以使用<\/[^>]+>,<br/>
要求匹配成对标签,那就需要使用反向引用

var regex = /<([^>]+)>[\d\D]*</\1>/;


#### 4. <span id = "title-4">操作符优先级</span><br/>
优先级从高到低<br/> 1. 转义符 \。
2. 括号和方括号 (...)、(?:...)、(?=...)、(?!...)、[...]。
3. 量词限定符 {m}、{m,n}、{m,}、?、*、+。
4. 位置和序列 ^ 、$、 \元字符、 一般字符。
5. 管道符 竖杠。 #### 5. <span id = "title-5">元字符转义</span><br/>
正则不会引发歧义,自然不需要转义。<br/>
需要视情况转义的字符 ^ $ . * + ? | \ / ( ) [ ] { } = ! : - , #### 6. <span id = "title-6">search,split,match,replace,test,exec </span><br/>
search和match,会把接受的字符串转换为正则<br/>
search有匹配结果返回第一个位置索引,无返回-1<br/> match:没有g,返回的是标准匹配格式,即,数组的第一个元素是整体匹配的内容,接下来是分组捕获的内容,然后是整体匹配的第一个下标,最后是输入的目标字符串。<br/>
有g,返回的是所有匹配的内容。<br/>
当没有匹配时,不管有无g,都返回null。<br/> exec:当正则没有g时,使用match返回的信息比较多。但是有g后,就没有关键的信息index了。
而exec方法就能解决这个问题,它能接着上一次匹配后继续匹配:<br/>
其中正则实例lastIndex属性,表示下一次匹配开始的位置。<br/>

var string = "2017.06.27";

var regex2 = /\b(\d+)\b/g;

console.log( regex2.exec(string) );

console.log( regex2.lastIndex);

console.log( regex2.exec(string) );

console.log( regex2.lastIndex);

console.log( regex2.exec(string) );

console.log( regex2.lastIndex);

console.log( regex2.exec(string) );

console.log( regex2.lastIndex);

// => ["2017", "2017", index: 0, input: "2017.06.27"]

// => 4

// => ["06", "06", index: 5, input: "2017.06.27"]

// => 7

// => ["27", "27", index: 8, input: "2017.06.27"]

// => 10

// => null

// => 0

split<br/>
第一,它可以有第二个参数,表示结果数组的最大长度:<br/>

var string = "html,css,javascript";

console.log( string.split(/,/, 2) );

// =>["html", "css"]

第二,正则使用分组时,结果数组中是包含分隔符的:<br/>

var string = "html,css,javascript";

console.log( string.split(/(,)/) );

// =>["html", ",", "css", ",", "javascript"]


replace <br/>
第一个参数是正则表达式<br/>
第二个参数是字符串时,如下的字符有特殊的含义:<br/>
>
$1,$2,...,$99 匹配第1~99个分组里捕获的文本
$& 匹配到的子串文本
$` 匹配到的子串的左边文本
$' 匹配到的子串的右边文本
$$ 美元符号
>

var text = "cat, bat, sat, fat";

var result = text.replace(/(.at)/g,"word ($1)"); //word (cat), word (bat), word (sat), word (fat)

function htmlEscape(text){

return text.replace(/[<>"&]/g, function(match, pos, originalText){

switch(match){

case "<": return "<";

case ">":return ">";

case "&":return "&";

case """:return """;

}

});

}

htmlEscape("<div class="gretting">

最新文章

  1. 把一个英语句子中的单词次序颠倒后输出。例如输入“how are you”,输出“you are how”;
  2. gdb 远程调试 android native 程序
  3. CoreCRM 开发实录 —— 单元测试之 Mock UserManager 和 SignInManager
  4. uva11584
  5. R-kmeans聚类算法
  6. JUI/DWZ介绍、简单使用
  7. js 工厂模式、简单模式、抽象模式
  8. AspnetCore WebApi使用Swagger简单入门
  9. 《CSAPP》读书笔记
  10. Android应用开发资源
  11. Liunx之Centos系统无人值守全自动化安装
  12. UFT12 更新模式
  13. selenium设置代理,基于chrome浏览器
  14. python之爬虫(三) Urllib库的基本使用
  15. 我的Android进阶之旅------>解决AES加密报错:java.security.InvalidKeyException: Unsupported key size: 18 bytes
  16. 关于大数据领域各个组件打包部署到集群运行的总结(含手动和maven)(博主推荐)
  17. Python 网络爬虫 007 (编程) 通过网站地图爬取目标站点的所有网页
  18. Solr linux安装
  19. 大数据学习——SparkStreaming整合Kafka完成网站点击流实时统计
  20. Java实现把两个数组合并为一个的方法总结

热门文章

  1. 用Atom打造简单的java编译器以及对于可能出现的问题解答
  2. Opengl4.5 中文手册—C
  3. LNMP环境源码搭建
  4. OpenVPN client端配置文件详细说明(转)
  5. PIC32MZ 通过USB在线升级 -- USB HID bootloader
  6. java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理
  7. 个人工作中ssd、audio python脚本总结
  8. HDU1789 Doing Homework again
  9. javaWeb学习笔记——关于交叉连接-内连接-左外连接-右外连接的区别
  10. c# 反射得到实体类的字段名称和值,DataTable转List&lt;T&gt;