JS正则表达式完整版

目录

引言

第一章 正则表达式字符匹配攻略

1 两种模糊匹配

2. 字符组

3. 量词

4. 多选分支

5. 案例分析

第1章 小结

第二章 正则表达式位置匹配攻略

1. 什么是位置呢?

2. 如何匹配位置呢?

3. 位置的特性

4. 相关案例

第二章小结

第三章 正则表达式括号的作用

1. 分组和分支结构

2. 引用分组

3. 反向引用

4. 非捕获分组

5. 相关案例

第三章小结

第4章 正则表达式回溯法原理

1. 没有回溯的匹配

2. 有回溯的匹配

3. 常见的回溯形式

第四章小结

第5章 正则表达式的拆分

1. 结构和操作符

2. 注意要点

3. 案例分析

第五章小结

第6章 正则表达式的构建

1. 平衡法则

2. 构建正则前提

3. 准确性

4. 效率

第六章小结

第七章 正则表达式编程

1. 正则表达式的四种操作

2. 相关API注意要点

3. 真实案例

第七章小结

后记

1. 需要注意的地方

2. 参考资料

3. 个人感悟


引言

亲爱的读者朋友,如果你点开了这篇文章,说明你对正则很感兴趣。

想必你也了解正则的重要性,在我看来正则表达式是衡量程序员水平的一个侧面标准。

关于正则表达式的教程,网上也有很多,相信你也看了一些。

与之不同的是,本文的目的是希望所有认真读完的童鞋们,都有实质性的提高。

本文内容共有七章,用JavaScript语言完整地讨论了正则表达式的方方面面。

如果觉得文章某块儿没有说明白清楚,欢迎留言,能力范围之内,老姚必做详细解答。

具体章节如下:

  • 引言
  • 第一章 正则表达式字符匹配攻略
  • 第二章 正则表达式位置匹配攻略
  • 第三章 正则表达式括号的作用
  • 第四章 正则表达式回溯法原理
  • 第五章 正则表达式的拆分
  • 第六章 正则表达式的构建
  • 第七章 正则表达式编程
  • 后记

下面简单地说说每一章都讨论了什么?

正则是匹配模式,要么匹配字符,要么匹配位置。

第1章和第2章以这个角度去讲解了正则的基础。

在正则中可以使用括号捕获数据,要么在API中进行分组引用,要么在正则里进行反向引用。

这是第3章的主题,讲解了正则中括号的作用。

学习正则表达式,是需要了解其匹配原理的。

第4章,讲解了正则了正则表达式的回溯法原理。另外在第6章里,也讲解了正则的表达式的整体工作原理。

不仅能看懂别人的正则,还要自己会写正则。

第5章,是从读的角度,去拆分一个正则表达式,而第6章是从写的角度,去构建一个正则表达式。

学习正则,是为了在真实世界里应用的。

第7章讲解了正则的用法,和相关API需要注意的地方。

如何阅读本文?

我的建议是阅读两遍。第一遍,不求甚解地快速阅读一遍。阅读过程中遇到的问题不妨记录下来,也许阅读完毕后就能解决很多。然后有时间的话,再带着问题去精读第二遍。

深呼吸,开始我们的正则表达式旅程吧。我在终点等你。

 

第一章 正则表达式字符匹配攻略

正则表达式是匹配模式,要么匹配字符,要么匹配位置。请记住这句话。

然而关于正则如何匹配字符的学习,大部分人都觉得这块比较杂乱。

毕竟元字符太多了,看起来没有系统性,不好记。本章就解决这个问题。

内容包括:

  1. 两种模糊匹配
  2. 字符组
  3. 量词
  4. 分支结构
  5.  案例分析

1 两种模糊匹配

如果正则只有精确匹配是没多大意义的,比如/hello/,也只能匹配字符串中的"hello"这个子串。

 
  1. var regex = /hello/;

  2. console.log( regex.test("hello") );

  3. // => true

  4.  

正则表达式之所以强大,是因为其能实现模糊匹配。

而模糊匹配,有两个方向上的“模糊”:横向模糊和纵向模糊。

1.1 横向模糊匹配

横向模糊指的是,一个正则可匹配的字符串的长度不是固定的,可以是多种情况的。

其实现的方式是使用量词。譬如{m,n},表示连续出现最少m次,最多n次。

比如/ab{2,5}c/表示匹配这样一个字符串:第一个字符是“a”,接下来是2到5个字符“b”,最后是字符“c”。测试如下:

 
  1. var regex = /ab{2,5}c/g;

  2. var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc";

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

  4. // => ["abbc", "abbbc", "abbbbc", "abbbbbc"]

  5.  

注意:案例中用的正则是/ab{2,5}c/g,后面多了g,它是正则的一个修饰符。表示全局匹配,即在目标字符串中按顺序找到满足匹配模式的所有子串,强调的是“所有”,而不只是“第一个”。g是单词global的首字母。

1.2 纵向模糊匹配

纵向模糊指的是,一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种可能。

其实现的方式是使用字符组。譬如[abc],表示该字符是可以字符“a”、“b”、“c”中的任何一个。

比如/a[123]b/可以匹配如下三种字符串:"a1b"、"a2b"、"a3b"。测试如下:

 
  1. var regex = /a[123]b/g;

  2. var string = "a0b a1b a2b a3b a4b";

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

  4. // => ["a1b", "a2b", "a3b"]

  5.  

以上就是本章讲的主体内容,只要掌握横向和纵向模糊匹配,就能解决很大部分正则匹配问题。

接下来的内容就是展开说了,如果对此都比较熟悉的话,可以跳过,直接看本章案例那节。

2. 字符组

需要强调的是,虽叫字符组(字符类),但只是其中一个字符。例如[abc],表示匹配一个字符,它可以是“a”、“b”、“c”之一。

2.1 范围表示法

如果字符组里的字符特别多的话,怎么办?可以使用范围表示法。

比如[123456abcdefGHIJKLM],可以写成[1-6a-fG-M]。用连字符-来省略和简写。

因为连字符有特殊用途,那么要匹配“a”、“-”、“z”这三者中任意一个字符,该怎么做呢?

不能写成[a-z],因为其表示小写字符中的任何一个字符。

可以写成如下的方式:[-az][az-][a\-z]。即要么放在开头,要么放在结尾,要么转义。总之不会让引擎认为是范围表示法就行了。

2.2 排除字符组

纵向模糊匹配,还有一种情形就是,某位字符可以是任何东西,但就不能是"a"、"b"、"c"。

此时就是排除字符组(反义字符组)的概念。例如[^abc],表示是一个除"a"、"b"、"c"之外的任意一个字符。字符组的第一位放^(脱字符),表示求反的概念。

当然,也有相应的范围表示法。

2.3 常见的简写形式

有了字符组的概念后,一些常见的符号我们也就理解了。因为它们都是系统自带的简写形式。

\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][^]中任何的一个。

3. 量词

量词也称重复。掌握{m,n}的准确含义后,只需要记住一些简写形式。

3.1 简写形式

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

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

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

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

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

3.2 贪婪匹配和惰性匹配

看如下的例子:

 
  1. var regex = /\d{2,5}/g;

  2. var string = "123 1234 12345 123456";

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

  4. // => ["123", "1234", "12345", "12345"]

  5.  

其中正则/\d{2,5}/,表示数字连续出现2到5次。会匹配2位、3位、4位、5位连续数字。

但是其是贪婪的,它会尽可能多的匹配。你能给我6个,我就要5个。你能给我3个,我就3要个。反正只要在能力范围内,越多越好。

我们知道有时贪婪不是一件好事(请看文章最后一个例子)。而惰性匹配,就是尽可能少的匹配:

 
  1. var regex = /\d{2,5}?/g;

  2. var string = "123 1234 12345 123456";

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

  4. // => ["12", "12", "34", "12", "34", "12", "34", "56"]

  5.  

其中/\d{2,5}?/表示,虽然2到5次都行,当2个就够的时候,就不在往下尝试了。

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

{m,n}?
{m,}?
??
+?
*?

对惰性匹配的记忆方式是:量词后面加个问号,问一问你知足了吗,你很贪婪吗?

4. 多选分支

一个模式可以实现横向和纵向模糊匹配。而多选分支可以支持多个子模式任选其一。

具体形式如下:(p1|p2|p3),其中p1p2p3是子模式,用|(管道符)分隔,表示其中任何之一。

例如要匹配"good"和"nice"可以使用/good|nice/。测试如下:

 
  1. var regex = /good|nice/g;

  2. var string = "good idea, nice try.";

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

  4. // => ["good", "nice"]

  5.  

但有个事实我们应该注意,比如我用/good|goodbye/,去匹配"goodbye"字符串时,结果是"good":

 
  1. var regex = /good|goodbye/g;

  2. var string = "goodbye";

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

  4. // => ["good"]

  5.  

而把正则改成/goodbye|good/,结果是:

 
  1. var regex = /goodbye|good/g;

  2. var string = "goodbye";

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

  4. // => ["goodbye"]

  5.  

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

5. 案例分析

匹配字符,无非就是字符组、量词和分支结构的组合使用罢了。

下面找几个例子演练一下(其中,每个正则并不是只有唯一写法):

5.1 匹配16进制颜色值

要求匹配:

#ffbbad

#Fc01DF

#FFF

#ffE

分析:

表示一个16进制字符,可以用字符组[0-9a-fA-F]

其中字符可以出现3或6次,需要是用量词和分支结构。

使用分支结构时,需要注意顺序。

正则如下:

 
  1. var regex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g;

  2. var string = "#ffbbad #Fc01DF #FFF #ffE";

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

  4. // => ["#ffbbad", "#Fc01DF", "#FFF", "#ffE"]

  5.  

5.2 匹配时间

以24小时制为例。

要求匹配:

23:59

02:07

分析:

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

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

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

正则如下:

 
  1. var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;

  2. console.log( regex.test("23:59") );

  3. console.log( regex.test("02:07") );

  4. // => true

  5. // => true

如果也要求匹配7:9,也就是说时分前面的0可以省略。

此时正则变成:

 
  1. var regex = /^(0?[0-9]|1[0-9]|[2][0-3]):(0?[0-9]|[1-5][0-9])$/;

  2. console.log( regex.test("23:59") );

  3. console.log( regex.test("02:07") );

  4. console.log( regex.test("7:9") );

  5. // => true

  6. // => true

  7. // => true

5.3 匹配日期

比如yyyy-mm-dd格式为例。

要求匹配:

2017-06-10

分析:

年,四位数字即可,可用[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])

正则如下:

 
  1. var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;

  2. console.log( regex.test("2017-06-10") );

  3. // => true

  4.  

5.4 window操作系统文件路径

要求匹配:

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

F:\study\javascript\regex\

F:\study\javascript

F:\

分析:

整体模式是: 盘符:\文件夹\文件夹\文件夹\

其中匹配F:\,需要使用[a-zA-Z]:\\,其中盘符不区分大小写,注意\字符需要转义。

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

另外“文件夹\”,可以出现任意次。也就是([^\\:*<>|"?\r\n/]+\\)*。其中括号提供子表达式。

路径的最后一部分可以是“文件夹”,没有\,因此需要添加([^\\:*<>|"?\r\n/]+)?

最后拼接成了一个看起来比较复杂的正则:

 
  1. var regex = /^[a-zA-Z]:\\([^\\:*<>|"?\r\n/]+\\)*([^\\:*<>|"?\r\n/]+)?$/;

  2. console.log( regex.test("F:\\study\\javascript\\regex\\regular expression.pdf") );

  3. console.log( regex.test("F:\\study\\javascript\\regex\\") );

  4. console.log( regex.test("F:\\study\\javascript") );

  5. console.log( regex.test("F:\\") );

  6. // => true

  7. // => true

  8. // => true

  9. // => true

其中,JS中字符串表示\时,也要转义。

5.5 匹配id

要求从

提取出id="container"。

可能最开始想到的正则是:

 
  1. var regex = /id=".*"/

  2. var string = '

    ';

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

  4. // => id="container" class="main"

  5.  

因为.是通配符,本身就匹配双引号的,而量词*又是贪婪的,当遇到container后面双引号时,不会停下来,会继续匹配,直到遇到最后一个双引号为止。

解决之道,可以使用惰性匹配:

 
  1. var regex = /id=".*?"/

  2. var string = '

    ';

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

  4. // => id="container"

  5.  

当然,这样也会有个问题。效率比较低,因为其匹配原理会涉及到“回溯”这个概念(这里也只是顺便提一下,第四章会详细说明)。可以优化如下:

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

  2. var string = '

    ';

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

  4. // => id="container"

第1章 小结

字符匹配相关的案例,挺多的,不一而足。

掌握字符组和量词就能解决大部分常见的情形,也就是说,当你会了这二者,JS正则算是入门了。

 

第二章 正则表达式位置匹配攻略

正则表达式是匹配模式,要么匹配字符,要么匹配位置。请记住这句话。

然而大部分人学习正则时,对于匹配位置的重视程度没有那么高。

本章讲讲正则匹配位置的总总。

内容包括:

  1. 什么是位置?
  2. 如何匹配位置?
  3. 位置的特性
  4. 几个应用实例分析

1. 什么是位置呢?

位置是相邻字符之间的位置。比如,下图中箭头所指的地方:

JS正则表达式完整版_第1张图片

2. 如何匹配位置呢?

在ES5中,共有6个锚字符:

^ $ \b \B (?=p) (?!p)

2.1 ^和$

^(脱字符)匹配开头,在多行匹配中匹配行开头。

$(美元符号)匹配结尾,在多行匹配中匹配行结尾。

比如我们把字符串的开头和结尾用"#"替换(位置可以替换成字符的!):

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

  2. console.log(result);

  3. // => "#hello#"

  4.  

多行匹配模式时,二者是行的概念,这个需要我们的注意:

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

  2. console.log(result);

  3. /*

  4. #I#

  5. #love#

  6. #javascript#

  7. */

  8.  

2.2 \b和\B

\b是单词边界,具体就是\w\W之间的位置,也包括\w^之间的位置,也包括\w$之间的位置。

比如一个文件名是"[JS] Lesson_01.mp4"中的\b,如下:

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

  2. console.log(result);

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

  4.  

为什么是这样呢?这需要仔细看看。

首先,我们知道,\w是字符组[0-9a-zA-Z_]的简写形式,即\w是字母数字或者下划线的中任何一个字符。而\W是排除字符组[^0-9a-zA-Z_]的简写形式,即\W\w以外的任何一个字符。

此时我们可以看看"[#JS#] #Lesson_01#.#mp4#"中的每一个"#",是怎么来的。

  • 第一个"#",两边是"["与"J",是\W\w之间的位置。
  • 第二个"#",两边是"S"与"]",也就是\w\W之间的位置。
  • 第三个"#",两边是空格与"L",也就是\W\w之间的位置。
  • 第四个"#",两边是"1"与".",也就是\w\W之间的位置。
  • 第五个"#",两边是"."与"m",也就是\W\w之间的位置。
  • 第六个"#",其对应的位置是结尾,但其前面的字符"4"是\w,即\w$之间的位置。

知道了\b的概念后,那么\B也就相对好理解了。

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

具体说来就是\w\w\W\W^\W\W$之间的位置。

比如上面的例子,把所有\B替换成"#":

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

  2. console.log(result);

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

  4.  

2.3 (?=p)和(?!p)

(?=p),其中p是一个子模式,即p前面的位置。

比如(?=l),表示'l'字符前面的位置,例如:

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

  2. console.log(result);

  3. // => "he#l#lo"

  4.  

(?!p)就是(?=p)的反面意思,比如:

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

  2.  
  3. console.log(result);

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

  5.  

二者的学名分别是positive lookahead和negative lookahead。

中文翻译分别是正向先行断言和负向先行断言。

ES6中,还支持positive lookbehind和negative lookbehind。

具体是(?<=p)(?

也有书上把这四个东西,翻译成环视,即看看右边或看看左边。

但一般书上,没有很好强调这四者是个位置。

比如(?=p),一般都理解成:要求接下来的字符与p匹配,但不能包括p的那些字符。

而在本人看来(?=p)就与^一样好理解,就是p前面的那个位置。

3. 位置的特性

对于位置的理解,我们可以理解成空字符""。

比如"hello"字符串等价于如下的形式:

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

  2.  

也等价于:

 
  1. "hello" == "" + "" + "hello"

  2.  

因此,把/^hello$/写成/^^hello$$$/,是没有任何问题的:

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

  2. console.log(result);

  3. // => true

  4.  

甚至可以写成更复杂的:

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

  2. console.log(result);

  3. // => true

  4.  

也就是说字符之间的位置,可以写成多个。

把位置理解空字符,是对位置非常有效的理解方式。

4. 相关案例

4.1 不匹配任何东西的正则

让你写个正则不匹配任何东西

easy,/.^/

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

4.2 数字的千位分隔符表示法

比如把"12345678",变成"12,345,678"。

可见是需要把相应的位置替换成","。

思路是什么呢?

4.2.1 弄出最后一个逗号

使用(?=\d{3}$)就可以做到:

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

  2. console.log(result);

  3. // => "12345,678"

  4.  

4.2.2 弄出所有的逗号

因为逗号出现的位置,要求后面3个数字一组,也就是\d{3}至少出现一次。

此时可以使用量词+

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

  2. console.log(result);

  3. // => "12,345,678"

  4.  

4.2.3 匹配其余案例

写完正则后,要多验证几个案例,此时我们会发现问题:

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

  2. console.log(result);

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

  4.  

因为上面的正则,仅仅表示把从结尾向前数,一但是3的倍数,就把其前面的位置替换成逗号。因此才会出现这个问题。

怎么解决呢?我们要求匹配的到这个位置不能是开头。

我们知道匹配开头可以使用^,但要求这个位置不是开头怎么办?

easy,(?!^),你想到了吗?测试如下:

 
  1. var string1 = "12345678",

  2. string2 = "123456789";

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

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

  6. console.log(result);

  7. // => "12,345,678"

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

  10. console.log(result);

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

  12.  

4.2.4 支持其他形式

如果要把"12345678 123456789"替换成"12,345,678 123,456,789"。

此时我们需要修改正则,把里面的开头^和结尾$,替换成\b

 
  1. var string = "12345678 123456789",

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

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

  5. console.log(result);

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

  7.  

其中(?!\b)怎么理解呢?

要求当前是一个位置,但不是\b前面的位置,其实(?!\b)说的就是\B

因此最终正则变成了:/\B(?=(\d{3})+\b)/g

4.3 验证密码问题

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

此题,如果写成多个正则来判断,比较容易。但要写成一个正则就比较困难。

那么,我们就来挑战一下。看看我们对位置的理解是否深刻。

4.3.1 简化

不考虑“但必须至少包括2种字符”这一条件。我们可以容易写出:

 
  1. var reg = /^[0-9A-Za-z]{6,12}$/;

  2.  

4.3.2 判断是否包含有某一种字符

假设,要求的必须包含数字,怎么办?此时我们可以使用(?=.*[0-9])来做。

因此正则变成:

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

  2.  

4.3.3 同时包含具体两种字符

比如同时包含数字和小写字母,可以用(?=.*[0-9])(?=.*[a-z])来做。

因此正则变成:

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

  2.  

4.3.4 解答

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

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

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

最终答案是:

 
  1. var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9A-Za-z]{6,12}$/;

  2. console.log( reg.test("1234567") ); // false 全是数字

  3. console.log( reg.test("abcdef") ); // false 全是小写字母

  4. console.log( reg.test("ABCDEFGH") ); // false 全是大写字母

  5. console.log( reg.test("ab23C") ); // false 不足6位

  6. console.log( reg.test("ABCDEF234") ); // true 大写字母和数字

  7. console.log( reg.test("abcdEF234") ); // true 三者都有

  8.  

4.3.5 解惑

上面的正则看起来比较复杂,只要理解了第二步,其余就全部理解了。

/(?=.*[0-9])^[0-9A-Za-z]{6,12}$/

对于这个正则,我们只需要弄明白(?=.*[0-9])^即可。

分开来看就是(?=.*[0-9])^

表示开头前面还有个位置(当然也是开头,即同一个位置,想想之前的空字符类比)。

(?=.*[0-9])表示该位置后面的字符匹配.*[0-9],即,有任何多个任意字符,后面再跟个数字。

翻译成大白话,就是接下来的字符,必须包含个数字。

4.3.6 另外一种解法

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

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

对应的正则是:

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

  2.  

三种“都不能”呢?

最终答案是:

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

  2. console.log( reg.test("1234567") ); // false 全是数字

  3. console.log( reg.test("abcdef") ); // false 全是小写字母

  4. console.log( reg.test("ABCDEFGH") ); // false 全是大写字母

  5. console.log( reg.test("ab23C") ); // false 不足6位

  6. console.log( reg.test("ABCDEF234") ); // true 大写字母和数字

  7. console.log( reg.test("abcdEF234") ); // true 三者都有

  8.  

第二章小结

位置匹配相关的案例,挺多的,不一而足。

掌握匹配位置的这6个锚字符,给我们解决正则问题一个新工具。

 

第三章 正则表达式括号的作用

不管哪门语言中都有括号。正则表达式也是一门语言,而括号的存在使这门语言更为强大。

对括号的使用是否得心应手,是衡量对正则的掌握水平的一个侧面标准。

括号的作用,其实三言两语就能说明白,括号提供了分组,便于我们引用它。

引用某个分组,会有两种情形:在JavaScript里引用它,在正则表达式里引用它。

本章内容虽相对简单,但我也要写长点。

内容包括:

  1. 分组和分支结构
  2. 捕获分组
  3. 反向引用
  4. 非捕获分组
  5. 相关案例

1. 分组和分支结构

这二者是括号最直觉的作用,也是最原始的功能。

1.1 分组

我们知道/a+/匹配连续出现的“a”,而要匹配连续出现的“ab”时,需要使用/(ab)+/

其中括号是提供分组功能,使量词+作用于“ab”这个整体,测试如下:

 
  1. var regex = /(ab)+/g;

  2. var string = "ababa abbb ababab";

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

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

  5.  

1.2 分支结构

而在多选分支结构(p1|p2)中,此处括号的作用也是不言而喻的,提供了子表达式的所有可能。

比如,要匹配如下的字符串:

I love JavaScript

I love Regular Expression

可以使用正则:

 
  1. var regex = /^I love (JavaScript|Regular Expression)$/;

  2. console.log( regex.test("I love JavaScript") );

  3. console.log( regex.test("I love Regular Expression") );

  4. // => true

  5. // => true

如果去掉正则中的括号,即/^I love JavaScript|Regular Expression$/,匹配字符串是"I love JavaScript"和"Regular Expression",当然这不是我们想要的。

2. 引用分组

这是括号一个重要的作用,有了它,我们就可以进行数据提取,以及更强大的替换操作。

而要使用它带来的好处,必须配合使用实现环境的API。

以日期为例。假设格式是yyyy-mm-dd的,我们可以先写一个简单的正则:

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

  2.  

然后再修改成括号版的:

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

  2.  

为什么要使用这个正则呢?

2.1 提取数据

比如提取出年、月、日,可以这么做:

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

  2. var string = "2017-06-12";

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

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

  5.  

match返回的一个数组,第一个元素是整体匹配结果,然后是各个分组(括号里)匹配的内容,然后是匹配下标,最后是输入的文本。(注意:如果正则是否有修饰符gmatch返回的数组格式是不一样的)。

另外也可以使用正则对象的exec方法:

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

  2. var string = "2017-06-12";

  3. console.log( regex.exec(string) );

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

  5.  

同时,也可以使用构造函数的全局属性$1$9来获取:

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

  2. var string = "2017-06-12";

  3.  
  4. regex.test(string); // 正则操作即可,例如

  5. //regex.exec(string);

  6. //string.match(regex);

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

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

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

  11.  

2.2 替换

比如,想把yyyy-mm-dd格式,替换成mm/dd/yyyy怎么做?

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

  2. var string = "2017-06-12";

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

  4. console.log(result);

  5. // => "06/12/2017"

  6.  

其中replace中的,第二个参数里用$1$2$3指代相应的分组。等价于如下的形式:

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

  2. var string = "2017-06-12";

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

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

  5. });

  6. console.log(result);

  7. // => "06/12/2017"

  8.  

也等价于:

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

  2. var string = "2017-06-12";

  3. var result = string.replace(regex, function(match, year, month, day) {

  4. return month + "/" + day + "/" + year;

  5. });

  6. console.log(result);

  7. // => "06/12/2017"

  8.  

3. 反向引用

除了使用相应API来引用分组,也可以在正则本身里引用分组。但只能引用之前出现的分组,即反向引用。

还是以日期为例。

比如要写一个正则支持匹配如下三种格式:

2016-06-12

2016/06/12

2016.06.12

最先可能想到的正则是:

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

  2. var string1 = "2017-06-12";

  3. var string2 = "2017/06/12";

  4. var string3 = "2017.06.12";

  5. var string4 = "2016-06/12";

  6. console.log( regex.test(string1) ); // true

  7. console.log( regex.test(string2) ); // true

  8. console.log( regex.test(string3) ); // true

  9. console.log( regex.test(string4) ); // true

  10.  

其中/.需要转义。虽然匹配了要求的情况,但也匹配"2016-06/12"这样的数据。

假设我们想要求分割符前后一致怎么办?此时需要使用反向引用:

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

  2. var string1 = "2017-06-12";

  3. var string2 = "2017/06/12";

  4. var string3 = "2017.06.12";

  5. var string4 = "2016-06/12";

  6. console.log( regex.test(string1) ); // true

  7. console.log( regex.test(string2) ); // true

  8. console.log( regex.test(string3) ); // true

  9. console.log( regex.test(string4) ); // false

  10.  

注意里面的\1,表示的引用之前的那个分组(-|\/|\.)。不管它匹配到什么(比如-),\1都匹配那个同样的具体某个字符。

我们知道了\1的含义后,那么\2\3的概念也就理解了,即分别指代第二个和第三个分组。

看到这里,此时,恐怕你会有三个问题。

3.1 括号嵌套怎么办?

以左括号(开括号)为准。比如:

 
  1. var regex = /^((\d)(\d(\d)))\1\2\3\4$/;

  2. var string = "1231231233";

  3. console.log( regex.test(string) ); // true

  4. console.log( RegExp.$1 ); // 123

  5. console.log( RegExp.$2 ); // 1

  6. console.log( RegExp.$3 ); // 23

  7. console.log( RegExp.$4 ); // 3

  8.  

我们可以看看这个正则匹配模式:

  • 第一个字符是数字,比如说1,
  • 第二个字符是数字,比如说2,
  • 第三个字符是数字,比如说3,
  • 接下来的是\1,是第一个分组内容,那么看第一个开括号对应的分组是什么,是123,
  • 接下来的是\2,找到第2个开括号,对应的分组,匹配的内容是1,
  • 接下来的是\3,找到第3个开括号,对应的分组,匹配的内容是23,
  • 最后的是\4,找到第3个开括号,对应的分组,匹配的内容是3。

这个问题,估计仔细看一下,就该明白了。

3.2 \10表示什么呢?

另外一个疑问可能是,即\10是表示第10个分组,还是\10呢?

答案是前者,虽然一个正则里出现\10比较罕见。测试如下:

 
  1. var regex = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(#) \10+/;

  2. var string = "123456789# ######"

  3. console.log( regex.test(string) );

  4. // => true

3.3 引用不存在的分组会怎样?

因为反向引用,是引用前面的分组,但我们在正则里引用了不存在的分组时,此时正则不会报错,只是匹配反向引用的字符本身。例如\2,就匹配"\2"。注意"\2"表示对"2"进行了转意。

 
  1. var regex = /\1\2\3\4\5\6\7\8\9/;

  2. console.log( regex.test("\1\2\3\4\5\6\7\8\9") );

  3. console.log( "\1\2\3\4\5\6\7\8\9".split("") );

  4.  

chrome浏览器打印的结果:

JS正则表达式完整版_第2张图片

4. 非捕获分组

之前文中出现的分组,都会捕获它们匹配到的数据,以便后续引用,因此也称他们是捕获型分组。

如果只想要括号最原始的功能,但不会引用它,即,既不在API里引用,也不在正则里反向引用。此时可以使用非捕获分组(?:p),例如本文第一个例子可以修改为:

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

  2. var string = "ababa abbb ababab";

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

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

  5.  

5. 相关案例

至此括号的作用已经讲完了,总结一句话,就是提供了可供我们使用的分组,如何用就看我们的了。

5.1 字符串trim方法模拟

trim方法是去掉字符串的开头和结尾的空白符。有两种思路去做。

第一种,匹配到开头和结尾的空白符,然后替换成空字符。如:

 
  1. function trim(str) {

  2. return str.replace(/^\s+|\s+$/g, '');

  3. }

  4. console.log( trim(" foobar ") );

  5. // => "foobar"

  6.  

第二种,匹配整个字符串,然后用引用来提取出相应的数据:

 
  1. function trim(str) {

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

  3. }

  4. console.log( trim(" foobar ") );

  5. // => "foobar"

  6.  

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

当然,前者效率高。

5.2 将每个单词的首字母转换为大写

 
  1. function titleize(str) {

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

  3. return c.toUpperCase();

  4. });

  5. }

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

  7. // => "My Name Is Epeli"

  8.  

思路是找到每个单词的首字母,当然这里不使用非捕获匹配也是可以的。

5.3 驼峰化

 
  1. function camelize(str) {

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

  3. return c ? c.toUpperCase() : '';

  4. });

  5. }

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

  7. // => "MozTransform"

  8.  

其中分组(.)表示首字母。单词的界定是,前面的字符可以是多个连字符、下划线以及空白符。正则后面的?的目的,是为了应对str尾部的字符可能不是单词字符,比如str是'-moz-transform    '。

5.4 中划线化

 
  1. function dasherize(str) {

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

  3. }

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

  5. // => "-moz-transform"

  6.  

驼峰化的逆过程。

5.5 html转义和反转义

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

  2. function escapeHTML(str) {

  3. var escapeChars = {

  4. '¢' : 'cent',

  5. '£' : 'pound',

  6. '¥' : 'yen',

  7. '€': 'euro',

  8. '©' :'copy',

  9. '®' : 'reg',

  10. '<' : 'lt',

  11. '>' : 'gt',

  12. '"' : 'quot',

  13. '&' : 'amp',

  14. '\'' : '#39'

  15. };

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

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

  18. });

  19. }

  20. console.log( escapeHTML('

    Blah blah blah
    ') );

  21. // => "<div>Blah blah blah</div>";

  22.  

其中使用了用构造函数生成的正则,然后替换相应的格式就行了,这个跟本章没多大关系。

倒是它的逆过程,使用了括号,以便提供引用,也很简单,如下:

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

  2. function unescapeHTML(str) {

  3. var htmlEntities = {

  4. nbsp: ' ',

  5. cent: '¢',

  6. pound: '£',

  7. yen: '¥',

  8. euro: '€',

  9. copy: '©',

  10. reg: '®',

  11. lt: '<',

  12. gt: '>',

  13. quot: '"',

  14. amp: '&',

  15. apos: '\''

  16. };

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

  18. if (key in htmlEntities) {

  19. return htmlEntities[key];

  20. }

  21. return match;

  22. });

  23. }

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

  25. // => "

    Blah blah blah
    "

  26.  

通过key获取相应的分组引用,然后作为对象的键。

5.6 匹配成对标签

要求匹配:

regular expression

laoyao bye bye

不匹配:

wrong!</p> </blockquote> <p>匹配一个开标签,可以使用正则<code><[^>]+></code>,</p> <p>匹配一个闭标签,可以使用<code><\/[^>]+></code>,</p> <p>但是要求匹配成对标签,那就需要使用反向引用,如:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /<([^>]+)>[\d\D]*<\/\1>/;</code></code></p> </li> <li> <p><code><code>var string1 = "<title>regular expression";

  • var string2 = "

    laoyao bye bye

    ";

  • var string3 = "wrong!</p>";</code></code></p> </li> <li> <p><code><code>console.log( regex.test(string1) ); // true</code></code></p> </li> <li> <p><code><code>console.log( regex.test(string2) ); // true</code></code></p> </li> <li> <p><code><code>console.log( regex.test(string3) ); // false</code></code></p> </li> <li> </li> </ol> <p>其中开标签<code><[^>]+></code>改成<code><([^>]+)></code>,使用括号的目的是为了后面使用反向引用,而提供分组。闭标签使用了反向引用,<code><\/\1></code>。</p> <p>另外<code>[\d\D]</code>的意思是,这个字符是数字或者不是数字,因此,也就是匹配任意字符的意思。</p> <h3 id="%E7%AC%AC%E4%B8%89%E7%AB%A0%E5%B0%8F%E7%BB%93">第三章小结</h3> <p>正则中使用括号的例子那可是太多了,不一而足。</p> <p>重点理解括号可以提供分组,我们可以提取数据,应该就可以了。</p> <p>例子中的代码,基本没做多少分析,相信你都能看懂的。</p> <p> </p> <h2 id="%E7%AC%AC4%E7%AB%A0%20%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%9B%9E%E6%BA%AF%E6%B3%95%E5%8E%9F%E7%90%86">第4章 正则表达式回溯法原理</h2> <p>学习正则表达式,是需要懂点儿匹配原理的。</p> <p>而研究匹配原理时,有两个字出现的频率比较高:“回溯”。</p> <p>听起来挺高大上,确实还有很多人对此不明不白的。</p> <p>因此,本章就简单扼要地说清楚回溯到底是什么东西。</p> <p>内容包括:</p> <ol> <li>没有回溯的匹配</li> <li>有回溯的匹配</li> <li>常见的回溯形式</li> </ol> <h3 id="1.%20%E6%B2%A1%E6%9C%89%E5%9B%9E%E6%BA%AF%E7%9A%84%E5%8C%B9%E9%85%8D">1. 没有回溯的匹配</h3> <p>假设我们的正则是<code>/ab{1,3}c/</code>,其可视化形式是:</p> <p><a href="http://img.e-com-net.com/image/info8/d2b14348816142b5bcc7f01ab4084892.jpg" target="_blank"><img alt="JS正则表达式完整版_第3张图片" class="has" src="http://img.e-com-net.com/image/info8/d2b14348816142b5bcc7f01ab4084892.jpg" width="249" height="110" style="border:1px solid black;"></a></p> <p>而当目标字符串是"abbbc"时,就没有所谓的“回溯”。其匹配过程是:</p> <p> </p> <p>其中子表达式<code>b{1,3}</code>表示“b”字符连续出现1到3次。</p> <h3 id="2.%20%E6%9C%89%E5%9B%9E%E6%BA%AF%E7%9A%84%E5%8C%B9%E9%85%8D">2. 有回溯的匹配</h3> <p>如果目标字符串是"abbc",中间就有回溯。</p> <p> </p> <p>图中第5步有红颜色,表示匹配不成功。此时<code>b{1,3}</code>已经匹配到了2个字符“b”,准备尝试第三个时,结果发现接下来的字符是“c”。那么就认为<code>b{1,3}</code>就已经匹配完毕。然后状态又回到之前的状态(即第6步,与第4步一样),最后再用子表达式<code>c</code>,去匹配字符“c”。当然,此时整个表达式匹配成功了。</p> <p>图中的第6步,就是“回溯”。</p> <p>你可能对此没有感觉,这里我们再举一个例子。正则是:</p> <p> </p> <p>目标字符串是"abbbc",匹配过程是:</p> <p> </p> <p>其中第7步和第10步是回溯。第7步与第4步一样,此时<code>b{1,3}</code>匹配了两个"b",而第10步与第3步一样,此时<code>b{1,3}</code>只匹配了一个"b",这也是<code>b{1,3}</code>的最终匹配结果。</p> <p>这里再看一个清晰的回溯,正则是:</p> <p> </p> <p>目标字符串是:"acd"ef,匹配过程是:</p> <p> </p> <p>图中省略了尝试匹配双引号失败的过程。可以看出<code>.*</code>是非常影响效率的。</p> <p>为了减少一些不必要的回溯,可以把正则修改为<code>/"[^"]*"/</code>。</p> <h3 id="3.%20%E5%B8%B8%E8%A7%81%E7%9A%84%E5%9B%9E%E6%BA%AF%E5%BD%A2%E5%BC%8F">3. 常见的回溯形式</h3> <p>正则表达式匹配字符串的这种方式,有个学名,叫回溯法。</p> <p>回 溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再 前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法, 就称作“回溯法”。(copy于百度百科)。</p> <p>本质上就是深度优先搜索算法。<strong>其中退到之前的某一步这一过程,我们称为“回溯”。</strong>从上面的描述过程中,可以看出,路走不通时,就会发生“回溯”。即,<strong>尝试匹配失败时,接下来的一步通常就是回溯。</strong></p> <p>道理,我们是懂了。那么JS中正则表达式会产生回溯的地方都有哪些呢?</p> <p><strong>3.1 贪婪量词</strong></p> <p>之前的例子都是贪婪量词相关的。比如<code>b{1,3}</code>,因为其是贪婪的,尝试可能的顺序是从多往少的方向去尝试。首先会尝试"bbb",然后再看整个正则是否能匹配。不能匹配时,吐出一个"b",即在"bb"的基础上,再继续尝试。如果还不行,再吐出一个,再试。如果还不行呢?只能说明匹配失败了。</p> <p>虽然局部匹配是贪婪的,但也要满足整体能正确匹配。否则,皮之不存,毛将焉附?</p> <p>此时我们不禁会问,如果当多个贪婪量词挨着存在,并相互有冲突时,此时会是怎样?</p> <p>答案是,先下手为强!因为深度优先搜索。测试如下:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "12345";</code></code></p> </li> <li> <p><code><code>var regex = /(\d{1,3})(\d{1,3})/;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex) );</code></code></p> </li> <li> <p><code><code>// => ["12345", "123", "45", index: 0, input: "12345"]</code></code></p> </li> <li> </li> </ol> <p>其中,前面的<code>\d{1,3}</code>匹配的是"123",后面的<code>\d{1,3}</code>匹配的是"45"。</p> <p><strong>3.2 惰性量词</strong></p> <p>惰性量词就是在贪婪量词后面加个问号。表示尽可能少的匹配,比如:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "12345";</code></code></p> </li> <li> <p><code><code>var regex = /(\d{1,3}?)(\d{1,3})/;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex) );</code></code></p> </li> <li> <p><code><code>// => ["1234", "1", "234", index: 0, input: "12345"]</code></code></p> </li> <li> </li> </ol> <p>其中<code>\d{1,3}?</code>只匹配到一个字符"1",而后面的<code>\d{1,3}</code>匹配了"234"。</p> <p>虽然惰性量词不贪,但也会有回溯的现象。比如正则是:</p> <p> </p> <p>目标字符串是"12345",匹配过程是:</p> <p><a href="http://img.e-com-net.com/image/info8/0ec00112b30c4b97b926ef75c9075ebd.webp" target="_blank"><img alt="JS正则表达式完整版_第4张图片" class="has" src="http://img.e-com-net.com/image/info8/0ec00112b30c4b97b926ef75c9075ebd.webp" width="600" height="417" style="border:1px solid black;"></a></p> <p>知道你不贪、很知足,但是为了整体匹配成,没办法,也只能给你多塞点了。因此最后<code>\d{1,3}?</code>匹配的字符是"12",是两个数字,而不是一个。</p> <p>3.3 <strong>分支结构</strong></p> <p>我们知道分支也是惰性的,比如<code>/can|candy/</code>,去匹配字符串"candy",得到的结果是"can",因为分支会一个一个尝试,如果前面的满足了,后面就不会再试验了。</p> <p>分支结构,可能前面的子模式会形成了局部匹配,如果接下来表达式整体不匹配时,仍会继续尝试剩下的分支。这种尝试也可以看成一种回溯。</p> <p>比如正则:</p> <p><a href="http://img.e-com-net.com/image/info8/eaecc33a0bdc41f282cd6209f36c9a51.webp" target="_blank"><img alt="JS正则表达式完整版_第5张图片" class="has" src="http://img.e-com-net.com/image/info8/eaecc33a0bdc41f282cd6209f36c9a51.webp" width="320" height="121" style="border:1px solid black;"></a></p> <p>目标字符串是"candy",匹配过程:</p> <p><a href="http://img.e-com-net.com/image/info8/befc350920224b2a932eed459f61717a.webp" target="_blank"><img alt="JS正则表达式完整版_第6张图片" class="has" src="http://img.e-com-net.com/image/info8/befc350920224b2a932eed459f61717a.webp" width="600" height="512" style="border:1px solid black;"></a></p> <p>上面第5步,虽然没有回到之前的状态,但仍然回到了分支结构,尝试下一种可能。所以,可以认为它是一种回溯的。</p> <h3 id="%E7%AC%AC%E5%9B%9B%E7%AB%A0%E5%B0%8F%E7%BB%93">第四章小结</h3> <p>其实回溯法,很容易掌握的。</p> <p>简单总结就是,正因为有多种可能,所以要一个一个试。直到,要么到某一步时,整体匹配成功了;要么最后都试完后,发现整体匹配不成功。</p> <ol> <li>贪婪量词“试”的策略是:买衣服砍价。价钱太高了,便宜点,不行,再便宜点。</li> <li>惰性量词“试”的策略是:卖东西加价。给少了,再多给点行不,还有点少啊,再给点。</li> <li>分支结构“试”的策略是:货比三家。这家不行,换一家吧,还不行,再换。</li> </ol> <p>既然有回溯的过程,那么匹配效率肯定低一些。相对谁呢?相对那些DFA引擎。</p> <p>而JS的正则引擎是NFA,NFA是“非确定型有限自动机”的简写。</p> <p>大部分语言中的正则都是NFA,为啥它这么流行呢?</p> <p>答:你别看我匹配慢,但是我编译快啊,而且我还有趣哦。</p> <p> </p> <h2 id="%E7%AC%AC5%E7%AB%A0%20%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%9A%84%E6%8B%86%E5%88%86">第5章 正则表达式的拆分</h2> <p>对于一门语言的掌握程度怎么样,可以有两个角度来衡量:读和写。</p> <p>不仅要求自己能解决问题,还要看懂别人的解决方案。代码是这样,正则表达式也是这样。</p> <p>正则这门语言跟其他语言有一点不同,它通常就是一大堆字符,而没有所谓“语句”的概念。</p> <p>如何能正确地把一大串正则拆分成一块一块的,成为了破解“天书”的关键。</p> <p>本章就解决这一问题,内容包括:</p> <ol> <li>结构和操作符</li> <li>注意要点</li> <li>案例分析</li> </ol> <h3 id="1.%20%E7%BB%93%E6%9E%84%E5%92%8C%E6%93%8D%E4%BD%9C%E7%AC%A6">1. 结构和操作符</h3> <p>编程语言一般都有操作符。只要有操作符,就会出现一个问题。当一大堆操作在一起时,先操作谁,又后操作谁呢?为了不产生歧义,就需要语言本身定义好操作顺序,即所谓的优先级。</p> <p>而在正则表达式中,操作符都体现在结构中,即由特殊字符和普通字符所代表的一个个特殊整体。</p> <p>JS正则表达式中,都有哪些结构呢?</p> <blockquote> 字符字面量、字符组、量词、锚字符、分组、选择分支、反向引用。 </blockquote> <p>具体含义简要回顾如下(如懂,可以略去不看):</p> <blockquote> <p><strong>字面量</strong>,匹配一个具体字符,包括不用转义的和需要转义的。比如a匹配字符"a",又比如<code>\n</code>匹配换行符,又比如<code>\.</code>匹配小数点。</p> <p><strong>字符组</strong>,匹配一个字符,可以是多种可能之一,比如<code>[0-9]</code>,表示匹配一个数字。也有<code>\d</code>的简写形式。另外还有反义字符组,表示可以是除了特定字符之外任何一个字符,比如<code>[^0-9]</code>,表示一个非数字字符,也有<code>\D</code>的简写形式。</p> <p><strong>量词</strong>,表示一个字符连续出现,比如<code>a{1,3}</code>表示“a”字符连续出现3次。另外还有常见的简写形式,比如<code>a+</code>表示“a”字符连续出现至少一次。</p> <p><strong>锚点</strong>,匹配一个位置,而不是字符。比如^匹配字符串的开头,又比如<code>\b</code>匹配单词边界,又比如<code>(?=\d)</code>表示数字前面的位置。</p> <p><strong>分组</strong>,用括号表示一个整体,比如<code>(ab)+</code>,表示"ab"两个字符连续出现多次,也可以使用非捕获分组<code>(?:ab)+</code>。</p> <p><strong>分支</strong>,多个子表达式多选一,比如<code>abc|bcd</code>,表达式匹配"abc"或者"bcd"字符子串。</p> <p><strong>反向引用</strong>,比如<code>\2</code>,表示引用第2个分组。</p> </blockquote> <p>其中涉及到的操作符有:</p> <blockquote> 1.转义符 <code>\</code> <br> 2.括号和方括号 <code>(...)</code>、 <code>(?:...)</code>、 <code>(?=...)</code>、 <code>(?!...)</code>、 <code>[...]</code> <br> 3.量词限定符 <code>{m}</code>、 <code>{m,n}</code>、 <code>{m,}</code>、 <code>?</code>、 <code>*</code>、 <code>+</code> <br> 4.位置和序列 <code>^</code> 、 <code>$</code>、 <code>\元字符</code>、 <code>一般字符</code> <br> 5. 管道符(竖杠) <code>|</code> </blockquote> <p>上面操作符的优先级从上至下,由高到低。</p> <p>这里,我们来分析一个正则:</p> <p><code>/ab?(c|de*)+|fg/</code></p> <ol> <li>由于括号的存在,所以,<code>(c|de*)</code>是一个整体结构。</li> <li>在<code>(c|de*)</code>中,注意其中的量词<code>*</code>,因此<code>e*</code>是一个整体结构。</li> <li>又因为分支结构“|”优先级最低,因此<code>c</code>是一个整体、而<code>de*</code>是另一个整体。</li> <li>同理,整个正则分成了 <code>a</code>、<code>b?</code>、<code>(...)+</code>、<code>f</code>、<code>g</code>。而由于分支的原因,又可以分成<code>ab?(c|de*)+</code>和<code>fg</code>这两部分。</li> </ol> <p>希望你没被我绕晕,上面的分析可用其可视化形式描述如下:</p> <p><a href="http://img.e-com-net.com/image/info8/53a1d43fc8fe40c99d36b8f32ec845b1.webp" target="_blank"><img alt="JS正则表达式完整版_第7张图片" class="has" src="http://img.e-com-net.com/image/info8/53a1d43fc8fe40c99d36b8f32ec845b1.webp" width="466" height="284" style="border:1px solid black;"></a></p> <h3 id="2.%20%E6%B3%A8%E6%84%8F%E8%A6%81%E7%82%B9">2. 注意要点</h3> <p>关于结构和操作符,还是有几点需要强调:</p> <p><strong>2.1 匹配字符串整体问题</strong></p> <p>因为是要匹配整个字符串,我们经常会在正则前后中加上锚字符<code>^</code>和<code>$</code>。</p> <p>比如要匹配目标字符串"abc"或者"bcd"时,如果一不小心,就会写成<code>/^abc|bcd$/</code>。</p> <p>而位置字符和字符序列优先级要比竖杠高,故其匹配的结构是:</p> <p> </p> <p>应该修改成:</p> <p> </p> <p><strong>2.2 量词连缀问题</strong></p> <p>假设,要匹配这样的字符串:</p> <blockquote> <p>1. 每个字符为a、b、c任选其一</p> <p>2. 字符串的长度是3的倍数</p> </blockquote> <p>此时正则不能想当然地写成<code>/^[abc]{3}+$/</code>,这样会报错,说<code>+</code>前面没什么可重复的:</p> <p> </p> <p>此时要修改成:</p> <p> </p> <p><strong>2.3 元字符转义问题</strong></p> <p>所谓元字符,就是正则中有特殊含义的字符。</p> <p>所有结构里,用到的元字符总结如下:</p> <blockquote> <code><strong>^</strong></code> <strong> </strong> <code><strong>$</strong></code> <strong> </strong> <code><strong>.</strong></code> <strong> </strong> <code><strong>*</strong></code> <strong> </strong> <code><strong>+</strong></code> <strong> </strong> <code><strong>?</strong></code> <strong> </strong> <code><strong>|</strong></code> <strong> </strong> <code><strong>\</strong></code> <strong> </strong> <code><strong>/</strong></code> <strong> </strong> <code><strong>(</strong></code> <strong> </strong> <code><strong>)</strong></code> <strong> </strong> <code><strong>[</strong></code> <strong> </strong> <code><strong>]</strong></code> <strong> </strong> <code><strong>{</strong></code> <strong> </strong> <code><strong>}</strong></code> <strong> </strong> <code><strong>=</strong></code> <strong> </strong> <code><strong>!</strong></code> <strong> </strong> <code><strong>:</strong></code> <strong> </strong> <code><strong>-</strong></code> <strong> </strong> <code><strong>,</strong></code> </blockquote> <p>当匹配上面的字符本身时,可以一律转义:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "^$.*+?|\\/[]{}=!:-,";</code></code></p> </li> <li> <p><code><code>var regex = /\^\$\.\*\+\?\|\\\/\[\]\{\}\=\!\:\-\,/;</code></code></p> </li> <li> <p><code><code>console.log( regex.test(string) ); </code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <p>其中<code>string</code>中的<code>\</code>字符也要转义的。</p> <p>另外,在<code>string</code>中,也可以把每个字符转义,当然,转义后的结果仍是本身:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "^$.*+?|\\/[]{}=!:-,";</code></code></p> </li> <li> <p><code><code>var string2 = "\^\$\.\*\+\?\|\\\/\[\]\{\}\=\!\:\-\,";</code></code></p> </li> <li> <p><code><code>console.log( string == string2 ); </code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <p>现在的问题是,是不是每个字符都需要转义呢?否,看情况。</p> <p><strong>2.3.1 字符组中的元字符</strong></p> <p>跟字符组相关的元字符有<code>[]</code>、<code>^</code>、<code>-</code>。因此在会引起歧义的地方进行转义。例如开头的<code>^</code>必须转义,不然会把整个字符组,看成反义字符组。</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "^$.*+?|\\/[]{}=!:-,";</code></code></p> </li> <li> <p><code><code>var regex = /[\^$.*+?|\\/\[\]{}=!:\-,]/g;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex) );</code></code></p> </li> <li> <p><code><code>// => ["^", "$", ".", "*", "+", "?", "|", "\", "/", "[", "]", "{", "}", "=", "!", ":", "-", ","]</code></code></p> </li> <li> </li> </ol> <p><strong>2.3.2 匹配“[abc]”和“{3,5}”</strong></p> <p>我们知道<code>[abc]</code>,是个字符组。如果要匹配字符串"[abc]"时,该怎么办?</p> <p>可以写成<code>/\[abc\]/</code>,也可以写成<code>/\[abc]/</code>,测试如下:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "[abc]";</code></code></p> </li> <li> <p><code><code>var regex = /\[abc]/g;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex)[0] ); </code></code></p> </li> <li> <p><code><code>// => "[abc]"</code></code></p> </li> <li> </li> </ol> <p>只需要在第一个方括号转义即可,因为后面的方括号构不成字符组,正则不会引发歧义,自然不需要转义。</p> <p>同理,要匹配字符串"{3,5}",只需要把正则写成<code>/\{3,5}/</code>即可。</p> <p>另外,我们知道量词有简写形式<code>{m,}</code>,却没有<code>{,n}</code>的情况。虽然后者不构成量词的形式,但此时并不会报错。当然,匹配的字符串也是"{,n}",测试如下:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "{,3}";</code></code></p> </li> <li> <p><code><code>var regex = /{,3}/g;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex)[0] ); </code></code></p> </li> <li> <p><code><code>// => "{,3}"</code></code></p> </li> <li> </li> </ol> <p><strong>2.3.3 其余情况</strong></p> <p>比如<code>=</code> <code>!</code> <code>:</code> <code>-</code> <code>,</code>等符号,只要不在特殊结构中,也不需要转义。</p> <p>但是,括号需要前后都转义的,如<code>/\(123\)/</code>。</p> <p>至于剩下的<code>^</code> <code>$</code> <code>.</code> <code>*</code> <code>+</code> <code>?</code> <code>|</code> <code>\</code> <code>/</code>等字符,只要不在字符组内,都需要转义的。</p> <h3 id="3.%20%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90">3. 案例分析</h3> <p>接下来分析两个例子,一个简单的,一个复杂的。</p> <p><strong>3.1 身份证</strong></p> <p>正则表达式是:</p> <p><code>/^(\d{15}|\d{17}[\dxX])$/</code></p> <p>因为竖杠“|”,的优先级最低,所以正则分成了两部分<code>\d{15}</code>和<code>\d{17}[\dxX]</code>。</p> <ul> <li><code>\d{15}</code>表示15位连续数字。</li> <li><code>\d{17}[\dxX]</code>表示17位连续数字,最后一位可以是数字可以大小写字母"x"。</li> </ul> <p>可视化如下:</p> <p> </p> <p><strong>3.2 IPV4地址</strong></p> <p>正则表达式是:</p> <p><code>/^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/</code></p> <p>这个正则,看起来非常吓人。但是熟悉优先级后,会立马得出如下的结构:</p> <p><code>((...)\.){3}(...)</code></p> <p>上面的两个<code>(...)</code>是一样的结构。表示匹配的是3位数字。因此整个结构是</p> <blockquote> 3位数.3位数.3位数.3位数 </blockquote> <p>然后再来分析<code>(...)</code>:</p> <p><code>(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])</code></p> <p>它是一个多选结构,分成5个部分:</p> <ul> <li><code>0{0,2}\d</code>,匹配一位数,包括0补齐的。比如,9、09、009;</li> <li><code>0?\d{2}</code>,匹配两位数,包括0补齐的,也包括一位数;</li> <li><code>1\d{2}</code>,匹配100到199;</li> <li><code>2[0-4]\d</code>,匹配200-249;</li> <li><code>25[0-5]</code>,匹配250-255。</li> </ul> <p>最后来看一下其可视化形式:</p> <p><a href="http://img.e-com-net.com/image/info8/024e4f6c48df406492d6346821e26835.jpg" target="_blank"><img alt="JS正则表达式完整版_第8张图片" class="has" src="http://img.e-com-net.com/image/info8/024e4f6c48df406492d6346821e26835.jpg" width="600" height="358" style="border:1px solid black;"></a></p> <h3 id="%E7%AC%AC%E4%BA%94%E7%AB%A0%E5%B0%8F%E7%BB%93">第五章小结</h3> <p>掌握正则表达式中的优先级后,再看任何正则应该都有信心分析下去了。</p> <p>至于例子,不一而足,没有写太多。</p> <p>这里稍微总结一下,竖杠的优先级最低,即最后运算。</p> <p>只要知道这一点,就能读懂大部分正则。</p> <p>另外关于元字符转义问题,当自己不确定与否时,尽管去转义,总之是不会错的。</p> <p> </p> <h2 id="%E7%AC%AC6%E7%AB%A0%20%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%9A%84%E6%9E%84%E5%BB%BA">第6章 正则表达式的构建</h2> <p>对于一门语言的掌握程度怎么样,可以有两个角度来衡量:读和写。</p> <p>不仅要看懂别人的解决方案,也要能独立地解决问题。代码是这样,正则表达式也是这样。</p> <p>与“读”相比,“写”往往更为重要,这个道理是不言而喻的。</p> <p>对正则的运用,首重就是:如何针对问题,构建一个合适的正则表达式?</p> <p>本章就解决该问题,内容包括:</p> <ol> <li>平衡法则</li> <li>构建正则前提</li> <li>准确性</li> <li> 效率</li> </ol> <h3 id="1.%20%E5%B9%B3%E8%A1%A1%E6%B3%95%E5%88%99">1. 平衡法则</h3> <p>构建正则有一点非常重要,需要做到下面几点的平衡:</p> <ol> <li>匹配预期的字符串</li> <li>不匹配非预期的字符串</li> <li>可读性和可维护性</li> <li>效率</li> </ol> <h3 id="2.%20%E6%9E%84%E5%BB%BA%E6%AD%A3%E5%88%99%E5%89%8D%E6%8F%90">2. 构建正则前提</h3> <p><strong>2.1 是否能使用正则</strong></p> <p>正则太强大了,以至于我们随便遇到一个操作字符串问题时,都会下意识地去想,用正则该怎么做。但我们始终要提醒自己,正则虽然强大,但不是万能的,很多看似很简单的事情,还是做不到的。</p> <p>比如匹配这样的字符串:1010010001....</p> <p>虽然很有规律,但是只靠正则就是无能为力。</p> <p><strong>2.2 是否有必要使用正则</strong></p> <p>要认识到正则的局限,不要去研究根本无法完成的任务。同时,也不能走入另一个极端:无所不用正则。能用字符串API解决的简单问题,就不该正则出马。</p> <ul> <li>比如,从日期中提取出年月日,虽然可以使用正则:</li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017-07-01";</code></code></p> </li> <li> <p><code><code>var regex = /^(\d{4})-(\d{2})-(\d{2})/;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex) );</code></code></p> </li> <li> <p><code><code>// => ["2017-07-01", "2017", "07", "01", index: 0, input: "2017-07-01"]</code></code></p> </li> <li> </li> </ol> <p>其实,可以使用字符串的<code>split</code>方法来做,即可:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017-07-01";</code></code></p> </li> <li> <p><code><code>var result = string.split("-");</code></code></p> </li> <li> <p><code><code>console.log( result );</code></code></p> </li> <li> <p><code><code>// => ["2017", "07", "01"]</code></code></p> </li> <li> </li> </ol> <ul> <li>比如,判断是否有问号,虽然可以使用:</li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "?id=xx&act=search";</code></code></p> </li> <li> <p><code><code>console.log( string.search(/\?/) );</code></code></p> </li> <li> <p><code><code>// => 0</code></code></p> </li> <li> </li> </ol> <p>其实,可以使用字符串的<code>indexOf</code>方法:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "?id=xx&act=search";</code></code></p> </li> <li> <p><code><code>console.log( string.indexOf("?") );</code></code></p> </li> <li> <p><code><code>// => 0</code></code></p> </li> <li> </li> </ol> <ul> <li>比如获取子串,虽然可以使用正则:</li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "JavaScript";</code></code></p> </li> <li> <p><code><code>console.log( string.match(/.{4}(.+)/)[1] );</code></code></p> </li> <li> <p><code><code>// => Script</code></code></p> </li> <li> </li> </ol> <p>其实,可以直接使用字符串的<code>substring</code>或<code>substr</code>方法来做:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "JavaScript";</code></code></p> </li> <li> <p><code><code>console.log( string.substring(4) );</code></code></p> </li> <li> <p><code><code>// => Script</code></code></p> </li> <li> </li> </ol> <p><strong>2.3 是否有必要构建一个复杂的正则</strong></p> <p>比如密码匹配问题,要求密码长度6-12位,由数字、小写字符和大写字母组成,但必须至少包括2种字符。</p> <p>在第2章里,我们写出了正则是:</p> <p><code>/(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/</code></p> <p>其实可以使用多个小正则来做:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex1 = /^[0-9A-Za-z]{6,12}$/;</code></code></p> </li> <li> <p><code><code>var regex2 = /^[0-9]{6,12}$/;</code></code></p> </li> <li> <p><code><code>var regex3 = /^[A-Z]{6,12}$/;</code></code></p> </li> <li> <p><code><code>var regex4 = /^[a-z]{6,12}$/;</code></code></p> </li> <li> <p><code><code>function checkPassword(string) {</code></code></p> </li> <li> <p><code><code>if (!regex1.test(string)) return false;</code></code></p> </li> <li> <p><code><code>if (regex2.test(string)) return false;</code></code></p> </li> <li> <p><code><code>if (regex3.test(string)) return false;</code></code></p> </li> <li> <p><code><code>if (regex4.test(string)) return false;</code></code></p> </li> <li> <p><code><code>return true;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> </li> </ol> <h3 id="3.%20%E5%87%86%E7%A1%AE%E6%80%A7">3. 准确性</h3> <p>所谓准确性,就是能匹配预期的目标,并且不匹配非预期的目标。</p> <p>这里提到了“预期”二字,那么我们就需要知道目标的组成规则。</p> <p>不然没法界定什么样的目标字符串是符合预期的,什么样的又不是符合预期的。</p> <p>下面将举例说明,当目标字符串构成比较复杂时,该如何构建正则,并考虑到哪些平衡。</p> <p><strong>3.1 匹配固定电话</strong></p> <p>比如要匹配如下格式的固定电话号码:</p> <blockquote> <p>055188888888</p> <p>0551-88888888</p> <p>(0551)88888888</p> </blockquote> <p>第一步,了解各部分的模式规则。</p> <p>上面的电话,总体上分为区号和号码两部分(不考虑分机号和+86的情形)。</p> <p>区号是0开头的3到4位数字,对应的正则是:<code>0\d{2,3}</code></p> <p>号码是非0开头的7到8位数字,对应的正则是:<code>[1-9]\d{6,7}</code></p> <p>因此,匹配055188888888的正则是:<code>/^0\d{2,3}[1-9]\d{6,7}$/</code></p> <p>匹配0551-88888888的正则是:<code>/^0\d{2,3}-[1-9]\d{6,7}$/</code></p> <p>匹配(0551)88888888的正则是:<code>/^\(0\d{2,3}\)[1-9]\d{6,7}$/</code></p> <p>第二步,明确形式关系。</p> <p>这三者情形是或的关系,可以构建分支:</p> <p><code>/^0\d{2,3}[1-9]\d{6,7}$|^0\d{2,3}-[1-9]\d{6,7}$|^\(0\d{2,3}\)[1-9]\d{6,7}$/</code></p> <p>提取公共部分:</p> <p><code>/^(0\d{2,3}|0\d{2,3}-|\(0\d{2,3}\))[1-9]\d{6,7}$/</code></p> <p>进一步简写:</p> <p><code>/^(0\d{2,3}-?|\(0\d{2,3}\))[1-9]\d{6,7}$/</code></p> <p>其可视化形式:</p> <p> </p> <p>上面的正则构建过程略显罗嗦,但是这样做,能保证正则是准确的。</p> <p>上述三种情形是或的关系,这一点很重要,不然很容易按字符是否出现的情形把正则写成:</p> <p><code>/^\(?0\d{2,3}\)?-?[1-9]\d{6,7}$/</code></p> <p>虽然也能匹配上述目标字符串,但也会匹配(0551-88888888这样的字符串。当然,这不是我们想要的。</p> <p>其实这个正则也不是完美的,因为现实中,并不是每个3位数和4位数都是一个真实的区号。</p> <p>这就是一个平衡取舍问题,一般够用就行。</p> <p><strong>3.2 匹配浮点数</strong></p> <p>要求匹配如下的格式:</p> <blockquote> <p>1.23、+1.23、-1.23</p> <p>10、+10、-10</p> <p>.2、+.2、-.2</p> </blockquote> <p>可以看出正则分为三部分。</p> <p>符号部分:<code>[+-]</code></p> <p>整数部分:<code>\d+</code></p> <p>小数部分:<code>\.\d+</code></p> <p>上述三个部分,并不是全部都出现。如果此时很容易写出如下的正则:</p> <p><code>/^[+-]?(\d+)?(\.\d+)?$/</code></p> <p>此正则看似没问题,但这个正则也会匹配空字符""。</p> <p>因为目标字符串的形式关系不是要求每部分都是可选的。</p> <p>要匹配1.23、+1.23、-1.23,可以用<code>/^[+-]?\d+\.\d+$/</code></p> <p>要匹配10、+10、-10,可以用<code>/^[+-]?\d+$/</code></p> <p>要匹配.2、+.2、-.2,可以用<code>/^[+-]?\.\d+$/</code></p> <p>因此整个正则是这三者的或的关系,提取公众部分后是:</p> <p><code>/^[+-]?(\d+\.\d+|\d+|\.\d+)$/</code></p> <p>其可视化形式是:</p> <p><a href="http://img.e-com-net.com/image/info8/ff3c3e971c5a4c9785a31da0511db919.jpg" target="_blank"><img alt="JS正则表达式完整版_第9张图片" class="has" src="http://img.e-com-net.com/image/info8/ff3c3e971c5a4c9785a31da0511db919.jpg" width="600" height="273" style="border:1px solid black;"></a></p> <p>如果要求不匹配+.2和-.2,此时正则变成:</p> <p><a href="http://img.e-com-net.com/image/info8/023bb9377d4640159626e3193840464c.jpg" target="_blank"><img alt="JS正则表达式完整版_第10张图片" class="has" src="http://img.e-com-net.com/image/info8/023bb9377d4640159626e3193840464c.jpg" width="600" height="294" style="border:1px solid black;"></a></p> <p>当然,<code>/^[+-]?(\d+\.\d+|\d+|\.\d+)$/</code>也不是完美的,我们也是做了些取舍,比如:</p> <ul> <li>它也会匹配012这样以0开头的整数。如果要求不匹配的话,需要修改整数部分的正则。</li> <li>一般进行验证操作之前,都要经过trim和判空。那样的话,也许那个错误正则也就够用了。</li> <li>也可以进一步改写成:<code>/^[+-]?(\d+)?(\.)?\d+$/</code>,这样我们就需要考虑可读性和可维护性了。</li> </ul> <h3 id="4.%20%E6%95%88%E7%8E%87">4. 效率</h3> <p>保证了准确性后,才需要是否要考虑要优化。大多数情形是不需要优化的,除非运行的非常慢。什么情形正则表达式运行才慢呢?我们需要考察正则表达式的运行过程(原理)。</p> <p>正则表达式的运行分为如下的阶段:</p> <ol> <li>编译</li> <li>设定起始位置</li> <li>尝试匹配</li> <li>匹配失败的话,从下一位开始继续第3步</li> <li>最终结果:匹配成功或失败</li> </ol> <p>下面以代码为例,来看看这几个阶段都做了什么:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /\d+/g;</code></code></p> </li> <li> <p><code><code>console.log( regex.lastIndex, regex.exec("123abc34def") );</code></code></p> </li> <li> <p><code><code>console.log( regex.lastIndex, regex.exec("123abc34def") );</code></code></p> </li> <li> <p><code><code>console.log( regex.lastIndex, regex.exec("123abc34def") );</code></code></p> </li> <li> <p><code><code>console.log( regex.lastIndex, regex.exec("123abc34def") );</code></code></p> </li> <li> <p><code><code>// => 0 ["123", index: 0, input: "123abc34def"]</code></code></p> </li> <li> <p><code><code>// => 3 ["34", index: 6, input: "123abc34def"]</code></code></p> </li> <li> <p><code><code>// => 8 null</code></code></p> </li> <li> <p><code><code>// => 0 ["123", index: 0, input: "123abc34def"]</code></code></p> </li> <li> </li> </ol> <p>具体分析如下:</p> <pre><code><code>var regex = /\d+/g;</code></code></pre> <p>当生成一个正则时,引擎会对其进行编译。报错与否出现这这个阶段。</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>regex.exec("123abc34def")</code></code></p> </li> <li> </li> </ol> <p>当尝试匹配时,需要确定从哪一位置开始匹配。一般情形都是字符串的开头,即第0位。</p> <p>但当使用<code>test</code>和<code>exec</code>方法,且正则有<code>g</code>时,起始位置是从正则对象的<code>lastIndex</code>属性开始。</p> <p>因此第一次<code>exec</code>是从第0位开始,而第二次是从3开始的。</p> <p>设定好起始位置后,就开始尝试匹配了。</p> <p>比如第一次<code>exec</code>,从0开始,去尝试匹配,并且成功地匹配到3个数字。此时结束时的下标是2,因此下一次的起始位置是3。</p> <p>而 第二次,起始下标是3,但第3个字符是“a”,并不是数字。但此时并不会直接报匹配失败,而是移动到下一位置,即从第4位开始继续尝试匹配,但该字符是 b,也不是数字。再移动到下一位,是c仍不行,再移动一位是数字3,此时匹配到了两位数字34。此时,下一次匹配的位置是d的位置,即第8位。</p> <p>第三次,是从第8位开始匹配,直到试到最后一位,也没发现匹配的,因此匹配失败,返回<code>null</code>。同时设置<code>lastIndex</code>为0,即,如要再尝试匹配的话,需从头开始。</p> <p>从上面可以看出,匹配会出现效率问题,主要出现在上面的第3阶段和第4阶段。</p> <p>因此,主要优化手法也是针对这两阶段的。</p> <p><strong>4.1 使用具体型字符组来代替通配符,来消除回溯</strong></p> <p>而在第三阶段,最大的问题就是回溯。</p> <p>例如,匹配双引用号之间的字符。如,匹配字符串123"abc"456中的"abc"。</p> <p>如果正则用的是:<code>/".*"/</code>,,会在第3阶段产生4次回溯(粉色表示<code>.*</code>匹配的内容):</p> <p><a href="http://img.e-com-net.com/image/info8/d2ba533045764fedbab36c38644645ca.webp" target="_blank"><img alt="JS正则表达式完整版_第11张图片" class="has" src="http://img.e-com-net.com/image/info8/d2ba533045764fedbab36c38644645ca.webp" width="600" height="658" style="border:1px solid black;"></a></p> <p>如果正则用的是:<code>/".*?"/</code>,会产生2次回溯(粉色表示<code>.*?</code>匹配的内容):</p> <p> </p> <p>因为回溯的存在,需要引擎保存多种可能中未尝试过的状态,以便后续回溯时使用。注定要占用一定的内存。</p> <p>此时要使用具体化的字符组,来代替通配符<code>.</code>,以便消除不必要的字符,此时使用正则<code>/"[^"]*"/</code>,即可。</p> <p><strong>4.2 使用非捕获型分组</strong></p> <p>因为括号的作用之一是,可以捕获分组和分支里的数据。那么就需要内存来保存它们。</p> <p>当我们不需要使用分组引用和反向引用时,此时可以使用非捕获分组。例如:</p> <p><code>/^[+-]?(\d+\.\d+|\d+|\.\d+)$/</code></p> <p>可以修改成:</p> <p><code>/^[+-]?(?:\d+\.\d+|\d+|\.\d+)$/</code></p> <p><strong>4.3 独立出确定字符</strong></p> <p>例如<code>/a+/</code>,可以修改成<code>/aa*/</code>。</p> <p>因为后者能比前者多确定了字符a。这样会在第四步中,加快判断是否匹配失败,进而加快移位的速度。</p> <p><strong>4.4 提取分支公共部分</strong></p> <p>比如/<code>^abc|^def/</code>,修改成<code>/^(?:abc|def)/</code>。</p> <p>又比如<code>/this|that/</code>,修改成<code>/th(?:is|at)/</code>。</p> <p>这样做,可以减少匹配过程中可消除的重复。</p> <p><strong>4.5 减少分支的数量,缩小它们的范围</strong></p> <p><code>/red|read/</code>,可以修改成<code>/rea?d/</code>。此时分支和量词产生的回溯的成本是不一样的。但这样优化后,可读性会降低的。</p> <h3 id="%E7%AC%AC%E5%85%AD%E7%AB%A0%E5%B0%8F%E7%BB%93">第六章小结</h3> <p>本章涉及的内容并不多。</p> <p>一般情况下,针对某问题能写出一个满足需求的正则,基本上就可以了。</p> <p>至于准确性和效率方面的追求,纯属看个人要求了。我觉得够用就行了。</p> <p>关于准确性,本章关心的是最常用的解决思路:</p> <p>针对每种情形,分别写出正则,然用分支把他们合并在一起,再提取分支公共部分,就能得到准确的正则。</p> <p>至于优化,本章没有为了凑数,去写一大堆。了解了匹配原理,常见的优化手法也就这么几种。</p> <p> </p> <h2 id="%E7%AC%AC%E4%B8%83%E7%AB%A0%20%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%BC%96%E7%A8%8B">第七章 正则表达式编程</h2> <p>什么叫知识,能指导我们实践的东西才叫知识。</p> <p>学习一样东西,如果不能使用,最多只能算作纸上谈兵。正则表达式的学习,也不例外。</p> <p>掌握了正则表达式的语法后,下一步,也是关键的一步,就是在真实世界中使用它。</p> <p>那么如何使用正则表达式呢?有哪些关键的点呢?本章就解决这个问题。</p> <p>内容包括:</p> <ol> <li>正则表达式的四种操作</li> <li>相关API注意要点</li> <li>真实案例</li> </ol> <h3 id="1.%20%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%9A%84%E5%9B%9B%E7%A7%8D%E6%93%8D%E4%BD%9C">1. 正则表达式的四种操作</h3> <p>正则表达式是匹配模式,不管如何使用正则表达式,万变不离其宗,都需要先“匹配”。</p> <p>有了匹配这一基本操作后,才有其他的操作:验证、切分、提取、替换。</p> <p>进行任何相关操作,也需要宿主引擎相关API的配合使用。当然,在JS中,相关API也不多。</p> <p><strong>1.1 验证</strong></p> <p>验证是正则表达式最直接的应用,比如表单验证。</p> <p>在说验证之前,先要说清楚匹配是什么概念。</p> <p>所谓匹配,就是看目标字符串里是否有满足匹配的子串。因此,“匹配”的本质就是“查找”。</p> <p>有没有匹配,是不是匹配上,判断是否的操作,即称为“验证”。</p> <p>这里举一个例子,来看看如何使用相关API进行验证操作的。</p> <p>比如,判断一个字符串中是否有数字。</p> <ul> <li>使用<code>search</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /\d/;</code></code></p> </li> <li> <p><code><code>var string = "abc123";</code></code></p> </li> <li> <p><code><code>console.log( !!~string.search(regex) );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <ul> <li>使用<code>test</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /\d/;</code></code></p> </li> <li> <p><code><code>var string = "abc123";</code></code></p> </li> <li> <p><code><code>console.log( regex.test(string) );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <ul> <li>使用<code>match</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /\d/;</code></code></p> </li> <li> <p><code><code>var string = "abc123";</code></code></p> </li> <li> <p><code><code>console.log( !!string.match(regex) );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <ul> <li>使用<code>exec</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /\d/;</code></code></p> </li> <li> <p><code><code>var string = "abc123";</code></code></p> </li> <li> <p><code><code>console.log( !!regex.exec(string) );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <p>其中,最常用的是<code>test</code>。</p> <p><strong>1.2 切分</strong></p> <p>匹配上了,我们就可以进行一些操作,比如切分。</p> <p>所谓“切分”,就是把目标字符串,切成一段一段的。在JS中使用的是<code>split</code>。</p> <p>比如,目标字符串是"html,css,javascript",按逗号来切分:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /,/;</code></code></p> </li> <li> <p><code><code>var string = "html,css,javascript";</code></code></p> </li> <li> <p><code><code>console.log( string.split(regex) );</code></code></p> </li> <li> <p><code><code>// => ["html", "css", "javascript"]</code></code></p> </li> <li> </li> </ol> <p>又比如,如下的日期格式:</p> <blockquote> <p>2017/06/26</p> <p>2017.06.26</p> <p>2017-06-26</p> </blockquote> <p>可以使用<code>split</code>“切出”年月日:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /\D/;</code></code></p> </li> <li> <p><code><code>console.log( "2017/06/26".split(regex) );</code></code></p> </li> <li> <p><code><code>console.log( "2017.06.26".split(regex) );</code></code></p> </li> <li> <p><code><code>console.log( "2017-06-26".split(regex) );</code></code></p> </li> <li> <p><code><code>// => ["2017", "06", "26"]</code></code></p> </li> <li> <p><code><code>// => ["2017", "06", "26"]</code></code></p> </li> <li> <p><code><code>// => ["2017", "06", "26"]</code></code></p> </li> <li> </li> </ol> <p><strong>1.3 提取</strong></p> <p>虽然整体匹配上了,但有时需要提取部分匹配的数据。</p> <p>此时正则通常要使用分组引用(分组捕获)功能,还需要配合使用相关API。</p> <p>这里,还是以日期为例,提取出年月日。注意下面正则中的括号:</p> <ul> <li><code>match</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;</code></code></p> </li> <li> <p><code><code>var string = "2017-06-26";</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex) );</code></code></p> </li> <li> <p><code><code>// =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]</code></code></p> </li> <li> </li> </ol> <ul> <li><code>exec</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;</code></code></p> </li> <li> <p><code><code>var string = "2017-06-26";</code></code></p> </li> <li> <p><code><code>console.log( regex.exec(string) );</code></code></p> </li> <li> <p><code><code>// =>["2017-06-26", "2017", "06", "26", index: 0, input: "2017-06-26"]</code></code></p> </li> <li> </li> </ol> <ul> <li><code>test</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;</code></code></p> </li> <li> <p><code><code>var string = "2017-06-26";</code></code></p> </li> <li> <p><code><code>regex.test(string);</code></code></p> </li> <li> <p><code><code>console.log( RegExp.$1, RegExp.$2, RegExp.$3 );</code></code></p> </li> <li> <p><code><code>// => "2017" "06" "26"</code></code></p> </li> <li> </li> </ol> <ul> <li><code>search</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;</code></code></p> </li> <li> <p><code><code>var string = "2017-06-26";</code></code></p> </li> <li> <p><code><code>string.search(regex);</code></code></p> </li> <li> <p><code><code>console.log( RegExp.$1, RegExp.$2, RegExp.$3 );</code></code></p> </li> <li> <p><code><code>// => "2017" "06" "26"</code></code></p> </li> <li> </li> </ol> <ul> <li><code>replace</code></li> </ul> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /^(\d{4})\D(\d{2})\D(\d{2})$/;</code></code></p> </li> <li> <p><code><code>var string = "2017-06-26";</code></code></p> </li> <li> <p><code><code>var date = [];</code></code></p> </li> <li> <p><code><code>string.replace(regex, function(match, year, month, day) {</code></code></p> </li> <li> <p><code><code>date.push(year, month, day);</code></code></p> </li> <li> <p><code><code>});</code></code></p> </li> <li> <p><code><code>console.log(date);</code></code></p> </li> <li> <p><code><code>// => ["2017", "06", "26"]</code></code></p> </li> <li> </li> </ol> <p>其中,最常用的是<code>match</code>。</p> <p><strong>1.4 替换</strong></p> <p>找,往往不是目的,通常下一步是为了替换。在JS中,使用<code>replace</code>进行替换。</p> <p>比如把日期格式,从yyyy-mm-dd替换成yyyy/mm/dd:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017-06-26";</code></code></p> </li> <li> <p><code><code>var today = new Date( string.replace(/-/g, "/") );</code></code></p> </li> <li> <p><code><code>console.log( today );</code></code></p> </li> <li> <p><code><code>// => Mon Jun 26 2017 00:00:00 GMT+0800 (中国标准时间)</code></code></p> </li> <li> </li> </ol> <p>这里只是简单地应用了一下<code>replace</code>。但,<code>replace</code>方法是强大的,是需要重点掌握的。</p> <h3 id="2.%20%E7%9B%B8%E5%85%B3API%E6%B3%A8%E6%84%8F%E8%A6%81%E7%82%B9">2. 相关API注意要点</h3> <p>从上面可以看出用于正则操作的方法,共有6个,字符串实例4个,正则实例2个:</p> <blockquote> <p>String#search</p> <p>String#split</p> <p>String#match</p> <p>String#replace</p> <p>RegExp#test</p> <p>RegExp#exec</p> </blockquote> <p>本文不打算详细地讲解它们的方方面面细节,具体可以参考《JavaScript权威指南》的第三部分。本文重点列出一些容易忽视的地方,以飨读者。</p> <p><strong>2.1 search和match的参数问题</strong></p> <p>我们知道字符串实例的那4个方法参数都支持正则和字符串。</p> <p>但<code>search</code>和<code>match</code>,会把字符串转换为正则的。</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017.06.27";</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( string.search(".") );</code></code></p> </li> <li> <p><code><code>// => 0</code></code></p> </li> <li> <p><code><code>//需要修改成下列形式之一</code></code></p> </li> <li> <p><code><code>console.log( string.search("\\.") );</code></code></p> </li> <li> <p><code><code>console.log( string.search(/\./) );</code></code></p> </li> <li> <p><code><code>// => 4</code></code></p> </li> <li> <p><code><code>// => 4</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( string.match(".") );</code></code></p> </li> <li> <p><code><code>// => ["2", index: 0, input: "2017.06.27"]</code></code></p> </li> <li> <p><code><code>//需要修改成下列形式之一</code></code></p> </li> <li> <p><code><code>console.log( string.match("\\.") );</code></code></p> </li> <li> <p><code><code>console.log( string.match(/\./) );</code></code></p> </li> <li> <p><code><code>// => [".", index: 4, input: "2017.06.27"]</code></code></p> </li> <li> <p><code><code>// => [".", index: 4, input: "2017.06.27"]</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( string.split(".") );</code></code></p> </li> <li> <p><code><code>// => ["2017", "06", "27"]</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( string.replace(".", "/") );</code></code></p> </li> <li> <p><code><code>// => "2017/06.27"</code></code></p> </li> <li> </li> </ol> <p><strong>2.2 match返回结果的格式问题</strong></p> <p><code>match</code>返回结果的格式,与正则对象是否有修饰符<code>g</code>有关。</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017.06.27";</code></code></p> </li> <li> <p><code><code>var regex1 = /\b(\d+)\b/;</code></code></p> </li> <li> <p><code><code>var regex2 = /\b(\d+)\b/g;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex1) );</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex2) );</code></code></p> </li> <li> <p><code><code>// => ["2017", "2017", index: 0, input: "2017.06.27"]</code></code></p> </li> <li> <p><code><code>// => ["2017", "06", "27"]</code></code></p> </li> <li> </li> </ol> <p>没有<code>g</code>,返回的是标准匹配格式,即,数组的第一个元素是整体匹配的内容,接下来是分组捕获的内容,然后是整体匹配的第一个下标,最后是输入的目标字符串。</p> <p>有<code>g</code>,返回的是所有匹配的内容。</p> <p>当没有匹配时,不管有无<code>g</code>,都返回<code>null</code>。</p> <p><strong>2.3 exec比match更强大</strong></p> <p>当正则没有<code>g</code>时,使用<code>match</code>返回的信息比较多。但是有<code>g</code>后,就没有关键的信息<code>index</code>了。</p> <p>而<code>exec</code>方法就能解决这个问题,它能接着上一次匹配后继续匹配:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017.06.27";</code></code></p> </li> <li> <p><code><code>var regex2 = /\b(\d+)\b/g;</code></code></p> </li> <li> <p><code><code>console.log( regex2.exec(string) );</code></code></p> </li> <li> <p><code><code>console.log( regex2.lastIndex);</code></code></p> </li> <li> <p><code><code>console.log( regex2.exec(string) );</code></code></p> </li> <li> <p><code><code>console.log( regex2.lastIndex);</code></code></p> </li> <li> <p><code><code>console.log( regex2.exec(string) );</code></code></p> </li> <li> <p><code><code>console.log( regex2.lastIndex);</code></code></p> </li> <li> <p><code><code>console.log( regex2.exec(string) );</code></code></p> </li> <li> <p><code><code>console.log( regex2.lastIndex);</code></code></p> </li> <li> <p><code><code>// => ["2017", "2017", index: 0, input: "2017.06.27"]</code></code></p> </li> <li> <p><code><code>// => 4</code></code></p> </li> <li> <p><code><code>// => ["06", "06", index: 5, input: "2017.06.27"]</code></code></p> </li> <li> <p><code><code>// => 7</code></code></p> </li> <li> <p><code><code>// => ["27", "27", index: 8, input: "2017.06.27"]</code></code></p> </li> <li> <p><code><code>// => 10</code></code></p> </li> <li> <p><code><code>// => null</code></code></p> </li> <li> <p><code><code>// => 0</code></code></p> </li> <li> </li> </ol> <p>其中正则实例<code>lastIndex</code>属性,表示下一次匹配开始的位置。</p> <p>比如第一次匹配了“2017”,开始下标是0,共4个字符,因此这次匹配结束的位置是3,下一次开始匹配的位置是4。</p> <p>从上述代码看出,在使用<code>exec</code>时,经常需要配合使用<code>while</code>循环:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017.06.27";</code></code></p> </li> <li> <p><code><code>var regex2 = /\b(\d+)\b/g;</code></code></p> </li> <li> <p><code><code>var result;</code></code></p> </li> <li> <p><code><code>while ( result = regex2.exec(string) ) {</code></code></p> </li> <li> <p><code><code>console.log( result, regex2.lastIndex );</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>// => ["2017", "2017", index: 0, input: "2017.06.27"] 4</code></code></p> </li> <li> <p><code><code>// => ["06", "06", index: 5, input: "2017.06.27"] 7</code></code></p> </li> <li> <p><code><code>// => ["27", "27", index: 8, input: "2017.06.27"] 10</code></code></p> </li> <li> </li> </ol> <p><strong>2.4 修饰符g,对exex和test的影响</strong></p> <p>上面提到了正则实例的<code>lastIndex</code>属性,表示尝试匹配时,从字符串的<code>lastIndex</code>位开始去匹配。</p> <p>字符串的四个方法,每次匹配时,都是从0开始的,即<code>lastIndex</code>属性始终不变。</p> <p>而正则实例的两个方法<code>exec</code>、<code>test</code>,当正则是全局匹配时,每一次匹配完成后,都会修改<code>lastIndex</code>。下面让我们以<code>test</code>为例,看看你是否会迷糊:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /a/g;</code></code></p> </li> <li> <p><code><code>console.log( regex.test("a"), regex.lastIndex );</code></code></p> </li> <li> <p><code><code>console.log( regex.test("aba"), regex.lastIndex );</code></code></p> </li> <li> <p><code><code>console.log( regex.test("ababc"), regex.lastIndex );</code></code></p> </li> <li> <p><code><code>// => true 1</code></code></p> </li> <li> <p><code><code>// => true 3</code></code></p> </li> <li> <p><code><code>// => false 0</code></code></p> </li> <li> </li> </ol> <p>注意上面代码中的第三次调用<code>test</code>,因为这一次尝试匹配,开始从下标<code>lastIndex</code>即3位置处开始查找,自然就找不到了。</p> <p>如果没有<code>g</code>,自然都是从字符串第0个字符处开始尝试匹配:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /a/;</code></code></p> </li> <li> <p><code><code>console.log( regex.test("a"), regex.lastIndex );</code></code></p> </li> <li> <p><code><code>console.log( regex.test("aba"), regex.lastIndex );</code></code></p> </li> <li> <p><code><code>console.log( regex.test("ababc"), regex.lastIndex );</code></code></p> </li> <li> <p><code><code>// => true 0</code></code></p> </li> <li> <p><code><code>// => true 0</code></code></p> </li> <li> <p><code><code>// => true 0</code></code></p> </li> <li> </li> </ol> <p><strong>2.5 test整体匹配时需要使用^和$</strong></p> <p>这个相对容易理解,因为<code>test</code>是看目标字符串中是否有子串匹配正则,即有部分匹配即可。</p> <p>如果,要整体匹配,正则前后需要添加开头和结尾:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>console.log( /123/.test("a123b") );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> <p><code><code>console.log( /^123$/.test("a123b") );</code></code></p> </li> <li> <p><code><code>// => false</code></code></p> </li> <li> <p><code><code>console.log( /^123$/.test("123") );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <p><strong>2.6 split相关注意事项</strong></p> <p><code>split</code>方法看起来不起眼,但要注意的地方有两个的。</p> <p>第一,它可以有第二个参数,表示结果数组的最大长度:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "html,css,javascript";</code></code></p> </li> <li> <p><code><code>console.log( string.split(/,/, 2) );</code></code></p> </li> <li> <p><code><code>// =>["html", "css"]</code></code></p> </li> <li> </li> </ol> <p>第二,正则使用分组时,结果数组中是包含分隔符的:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "html,css,javascript";</code></code></p> </li> <li> <p><code><code>console.log( string.split(/(,)/) );</code></code></p> </li> <li> <p><code><code>// =>["html", ",", "css", ",", "javascript"]</code></code></p> </li> <li> </li> </ol> <p><strong>2.7 replace是很强大的</strong></p> <p>《JavaScript权威指南》认为<code>exec</code>是这6个API中最强大的,而我始终认为<code>replace</code>才是最强大的。因为它也能拿到该拿到的信息,然后可以假借替换之名,做些其他事情。</p> <p>总体来说<code>replace</code>有两种使用形式,这是因为它的第二个参数,可以是字符串,也可以是函数。</p> <p>当第二个参数是字符串时,如下的字符有特殊的含义:</p> <blockquote> <code>$1</code>, <code>$2</code>,..., <code>$99 </code>匹配第1~99个分组里捕获的文本 <br> <code>$&</code> 匹配到的子串文本 <br> <code>$`</code> 匹配到的子串的左边文本 <br> <code>$'</code> 匹配到的子串的右边文本 <br> <code>$$</code> 美元符号 </blockquote> <p>例如,把"2,3,5",变成"5=2+3":</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var result = "2,3,5".replace(/(\d+),(\d+),(\d+)/, "$3=$1+$2");</code></code></p> </li> <li> <p><code><code>console.log(result);</code></code></p> </li> <li> <p><code><code>// => "5=2+3"</code></code></p> </li> <li> </li> </ol> <p>又例如,把"2,3,5",变成"222,333,555":</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var result = "2,3,5".replace(/(\d+)/g, "$&$&$&");</code></code></p> </li> <li> <p><code><code>console.log(result);</code></code></p> </li> <li> <p><code><code>// => "222,333,555"</code></code></p> </li> <li> </li> </ol> <p>再例如,把"2+3=5",变成"2+3=2+3=5=5":</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var result = "2+3=5".replace(/=/, "$&$`$&$'$&");</code></code></p> </li> <li> <p><code><code>console.log(result);</code></code></p> </li> <li> <p><code><code>// => "2+3=2+3=5=5"</code></code></p> </li> <li> </li> </ol> <p>当第二个参数是函数时,我们需要注意该回调函数的参数具体是什么:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>"1234 2345 3456".replace(/(\d)\d{2}(\d)/g, function(match, $1, $2, index, input) {</code></code></p> </li> <li> <p><code><code>console.log([match, $1, $2, index, input]);</code></code></p> </li> <li> <p><code><code>});</code></code></p> </li> <li> <p><code><code>// => ["1234", "1", "4", 0, "1234 2345 3456"]</code></code></p> </li> <li> <p><code><code>// => ["2345", "2", "5", 5, "1234 2345 3456"]</code></code></p> </li> <li> <p><code><code>// => ["3456", "3", "6", 10, "1234 2345 3456"]</code></code></p> </li> <li> </li> </ol> <p>此时我们可以看到<code>replace</code>拿到的信息,并不比<code>exec</code>少。</p> <p><strong>2.8 使用构造函数需要注意的问题</strong></p> <p>一般不推荐使用构造函数生成正则,而应该优先使用字面量。因为用构造函数会多写很多<code>\</code>。</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var string = "2017-06-27 2017.06.27 2017/06/27";</code></code></p> </li> <li> <p><code><code>var regex = /\d{4}(-|\.|\/)\d{2}\1\d{2}/g;</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex) );</code></code></p> </li> <li> <p><code><code>// => ["2017-06-27", "2017.06.27", "2017/06/27"]</code></code></p> </li> <li> </li> <li> <p><code><code>regex = new RegExp("\\d{4}(-|\\.|\\/)\\d{2}\\1\\d{2}", "g");</code></code></p> </li> <li> <p><code><code>console.log( string.match(regex) );</code></code></p> </li> <li> <p><code><code>// => ["2017-06-27", "2017.06.27", "2017/06/27"]</code></code></p> </li> <li> </li> </ol> <p><strong>2.9 修饰符</strong></p> <p>ES5中修饰符,共3个:</p> <blockquote> <p><code>g</code> 全局匹配,即找到所有匹配的,单词是global</p> <p><code>i</code> 忽略字母大小写,单词ingoreCase</p> <p><code>m</code> 多行匹配,只影响<code>^</code>和<code>$</code>,二者变成行的概念,即行开头和行结尾。单词是multiline</p> </blockquote> <p>当然正则对象也有相应的只读属性:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /\w/img;</code></code></p> </li> <li> <p><code><code>console.log( regex.global );</code></code></p> </li> <li> <p><code><code>console.log( regex.ignoreCase );</code></code></p> </li> <li> <p><code><code>console.log( regex.multiline );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <p><strong>2.10 source属性</strong></p> <p>正则实例对象属性,除了<code>global</code>、<code>ingnoreCase</code>、<code>multiline</code>、<code>lastIndex</code>属性之外,还有一个<code>source</code>属性。</p> <p>它什么时候有用呢?</p> <p>比如,在构建动态的正则表达式时,可以通过查看该属性,来确认构建出的正则到底是什么:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var className = "high";</code></code></p> </li> <li> <p><code><code>var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");</code></code></p> </li> <li> <p><code><code>console.log( regex.source )</code></code></p> </li> <li> <p><code><code>// => (^|\s)high(\s|$) 即字符串"(^|\\s)high(\\s|$)"</code></code></p> </li> <li> </li> </ol> <p><strong>2.11 构造函数属性</strong></p> <p>构造函数的静态属性基于所执行的最近一次正则操作而变化。除了是<code>$1</code>,...,<code>$9</code>之外,还有几个不太常用的属性(有兼容性问题):</p> <blockquote> <code>RegExp.input</code> 最近一次目标字符串,简写成 <code>RegExp["$_"]</code> <br> <code>RegExp.lastMatch</code> 最近一次匹配的文本,简写成 <code>RegExp["$&"]</code> <br> <code>RegExp.lastParen</code> 最近一次捕获的文本,简写成 <code>RegExp["$+"]</code> <br> <code>RegExp.leftContext</code> 目标字符串中 <code>lastMatch</code>之前的文本,简写成 <code>RegExp["$`"]</code> <br> <code>RegExp.rightContext </code>目标字符串中 <code>lastMatch</code>之后的文本,简写成 <code>RegExp["$'"]</code> </blockquote> <p>测试代码如下:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var regex = /([abc])(\d)/g;</code></code></p> </li> <li> <p><code><code>var string = "a1b2c3d4e5";</code></code></p> </li> <li> <p><code><code>string.match(regex);</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( RegExp.input );</code></code></p> </li> <li> <p><code><code>console.log( RegExp["$_"]);</code></code></p> </li> <li> <p><code><code>// => "a1b2c3d4e5"</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( RegExp.lastMatch );</code></code></p> </li> <li> <p><code><code>console.log( RegExp["$&"] );</code></code></p> </li> <li> <p><code><code>// => "c3"</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( RegExp.lastParen );</code></code></p> </li> <li> <p><code><code>console.log( RegExp["$+"] );</code></code></p> </li> <li> <p><code><code>// => "3"</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( RegExp.leftContext );</code></code></p> </li> <li> <p><code><code>console.log( RegExp["$`"] );</code></code></p> </li> <li> <p><code><code>// => "a1b2"</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( RegExp.rightContext );</code></code></p> </li> <li> <p><code><code>console.log( RegExp["$'"] );</code></code></p> </li> <li> <p><code><code>// => "d4e5"</code></code></p> </li> </ol> <h3 id="3.%20%E7%9C%9F%E5%AE%9E%E6%A1%88%E4%BE%8B">3. 真实案例</h3> <p><strong>3.1 使用构造函数生成正则表达式</strong></p> <p>我们知道要优先使用字面量来创建正则,但有时正则表达式的主体是不确定的,此时可以使用构造函数来创建。模拟<code>getElementsByClassName</code>方法,就是很能说明该问题的一个例子。</p> <p>这里<code>getElementsByClassName</code>函数的实现思路是:</p> <ul> <li>比如要获取className为"high"的dom元素;</li> <li>首先生成一个正则:<code>/(^|\s)high(\s|$)/</code>;</li> <li>然后再用其逐一验证页面上的所有dom元素的类名,拿到满足匹配的元素即可。</li> </ul> <p>代码如下(可以直接复制到本地查看运行效果):</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code><p class="high">1111</p></code></code></p> </li> <li> <p><code><code><p class="high">2222</p></code></code></p> </li> <li> <p><code><code><p>3333</p></code></code></p> </li> <li> <p><code><code><script></code></code></p> </li> <li> <p><code><code>function getElementsByClassName(className) {</code></code></p> </li> <li> <p><code><code>var elements = document.getElementsByTagName("*");</code></code></p> </li> <li> <p><code><code>var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");</code></code></p> </li> <li> <p><code><code>var result = [];</code></code></p> </li> <li> <p><code><code>for (var i = 0; i < elements.length; i++) {</code></code></p> </li> <li> <p><code><code>var element = elements[i];</code></code></p> </li> <li> <p><code><code>if (regex.test(element.className)) {</code></code></p> </li> <li> <p><code><code>result.push(element)</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>return result;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>var highs = getElementsByClassName('high');</code></code></p> </li> <li> <p><code><code>highs.forEach(function(item) {</code></code></p> </li> <li> <p><code><code>item.style.color = 'red';</code></code></p> </li> <li> <p><code><code>});</code></code></p> </li> <li> <p><code><code></script></code></code></p> </li> <li> </li> </ol> <p><strong>3.2 使用字符串保存数据</strong></p> <p>一般情况下,我们都愿意使用数组来保存数据。但我看到有的框架中,使用的却是字符串。</p> <p>使用时,仍需要把字符串切分成数组。虽然不一定用到正则,但总感觉酷酷的,这里分享如下:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var utils = {};</code></code></p> </li> <li> <p><code><code>"Boolean|Number|String|Function|Array|Date|RegExp|Object|Error".split("|").forEach(function(item) {</code></code></p> </li> <li> <p><code><code>utils["is" + item] = function(obj) {</code></code></p> </li> <li> <p><code><code>return {}.toString.call(obj) == "[object " + item + "]";</code></code></p> </li> <li> <p><code><code>};</code></code></p> </li> <li> <p><code><code>});</code></code></p> </li> <li> <p><code><code>console.log( utils.isArray([1, 2, 3]) );</code></code></p> </li> <li> <p><code><code>// => true</code></code></p> </li> <li> </li> </ol> <p><strong>3.3 if语句中使用正则替代&&</strong></p> <p>比如,模拟<code>ready</code>函数,即加载完毕后再执行回调(不兼容ie的):</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>var readyRE = /complete|loaded|interactive/;</code></code></p> </li> <li> </li> <li> <p><code><code>function ready(callback) {</code></code></p> </li> <li> <p><code><code>if (readyRE.test(document.readyState) && document.body) {</code></code></p> </li> <li> <p><code><code>callback()</code></code></p> </li> <li> <p><code><code>} </code></code></p> </li> <li> <p><code><code>else {</code></code></p> </li> <li> <p><code><code>document.addEventListener(</code></code></p> </li> <li> <p><code><code>'DOMContentLoaded', </code></code></p> </li> <li> <p><code><code>function () {</code></code></p> </li> <li> <p><code><code>callback()</code></code></p> </li> <li> <p><code><code>},</code></code></p> </li> <li> <p><code><code>false</code></code></p> </li> <li> <p><code><code>);</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>};</code></code></p> </li> <li> <p><code><code>ready(function() {</code></code></p> </li> <li> <p><code><code>alert("加载完毕!")</code></code></p> </li> <li> <p><code><code>});</code></code></p> </li> <li> </li> </ol> <p><strong>3.4 使用强大的replace</strong></p> <p>因为<code>replace</code>方法比较强大,有时用它根本不是为了替换,只是拿其匹配到的信息来做文章。</p> <p>这里以查询字符串(querystring)压缩技术为例,注意下面<code>replace</code>方法中,回调函数根本没有返回任何东西。</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code>function compress(source) {</code></code></p> </li> <li> <p><code><code>var keys = {};</code></code></p> </li> <li> <p><code><code>source.replace(/([^=&]+)=([^&]*)/g, function(full, key, value) {</code></code></p> </li> <li> <p><code><code>keys[key] = (keys[key] ? keys[key] + ',' : '') + value;</code></code></p> </li> <li> <p><code><code>});</code></code></p> </li> <li> <p><code><code>var result = [];</code></code></p> </li> <li> <p><code><code>for (var key in keys) {</code></code></p> </li> <li> <p><code><code>result.push(key + '=' + keys[key]);</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>return result.join('&');</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> </li> <li> <p><code><code>console.log( compress("a=1&b=2&a=3&b=4") );</code></code></p> </li> <li> <p><code><code>// => "a=1,3&b=2,4"</code></code></p> </li> <li> </li> </ol> <p><strong>3.5 综合运用</strong></p> <p>最后这里再做个简单实用的正则测试器。</p> <p>具体效果如下:</p> <p> </p> <p>代码,直接贴了,相信你能看得懂:</p> <pre><code class="language-html hljs"> </code></pre> <ol> <li> <p><code><code><section></code></code></p> </li> <li> <p><code><code><div id="err"></div></code></code></p> </li> <li> <p><code><code><input id="regex" placeholder="请输入正则表达式"></code></code></p> </li> <li> <p><code><code><input id="text" placeholder="请输入测试文本"></code></code></p> </li> <li> <p><code><code><button id="run">测试一下</button></code></code></p> </li> <li> <p><code><code><div id="result"></div></code></code></p> </li> <li> <p><code><code></section></code></code></p> </li> <li> <p><code><code><style></code></code></p> </li> <li> <p><code><code>section{</code></code></p> </li> <li> <p><code><code>display:flex;</code></code></p> </li> <li> <p><code><code>flex-direction:column;</code></code></p> </li> <li> <p><code><code>justify-content:space-around;</code></code></p> </li> <li> <p><code><code>height:300px;</code></code></p> </li> <li> <p><code><code>padding:0 200px;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>section *{</code></code></p> </li> <li> <p><code><code>min-height:30px;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>#err {</code></code></p> </li> <li> <p><code><code>color:red;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>#result{</code></code></p> </li> <li> <p><code><code>line-height:30px;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>.info {</code></code></p> </li> <li> <p><code><code>background:#00c5ff;</code></code></p> </li> <li> <p><code><code>padding:2px;</code></code></p> </li> <li> <p><code><code>margin:2px;</code></code></p> </li> <li> <p><code><code>display:inline-block;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code></style></code></code></p> </li> <li> <p><code><code><script></code></code></p> </li> <li> <p><code><code>(function() {</code></code></p> </li> <li> <p><code><code>// 获取相应dom元素</code></code></p> </li> <li> <p><code><code>var regexInput = document.getElementById("regex");</code></code></p> </li> <li> <p><code><code>var textInput = document.getElementById("text");</code></code></p> </li> <li> <p><code><code>var runBtn = document.getElementById("run");</code></code></p> </li> <li> <p><code><code>var errBox = document.getElementById("err");</code></code></p> </li> <li> <p><code><code>var resultBox = document.getElementById("result");</code></code></p> </li> <li> </li> <li> <p><code><code>// 绑定点击事件</code></code></p> </li> <li> <p><code><code>runBtn.onclick = function() {</code></code></p> </li> <li> <p><code><code>// 清除错误和结果</code></code></p> </li> <li> <p><code><code>errBox.innerHTML = "";</code></code></p> </li> <li> <p><code><code>resultBox.innerHTML = "";</code></code></p> </li> <li> </li> <li> <p><code><code>// 获取正则和文本</code></code></p> </li> <li> <p><code><code>var text = textInput.value;</code></code></p> </li> <li> <p><code><code>var regex = regexInput.value;</code></code></p> </li> <li> </li> <li> <p><code><code>if (regex == "") {</code></code></p> </li> <li> <p><code><code>errBox.innerHTML = "请输入正则表达式";</code></code></p> </li> <li> <p><code><code>} else if (text == "") {</code></code></p> </li> <li> <p><code><code>errBox.innerHTML = "请输入测试文本";</code></code></p> </li> <li> <p><code><code>} else {</code></code></p> </li> <li> <p><code><code>regex = createRegex(regex);</code></code></p> </li> <li> <p><code><code>if (!regex) return;</code></code></p> </li> <li> <p><code><code>var result, results = [];</code></code></p> </li> <li> </li> <li> <p><code><code>// 没有修饰符g的话,会死循环</code></code></p> </li> <li> <p><code><code>if (regex.global) {</code></code></p> </li> <li> <p><code><code>while(result = regex.exec(text)) {</code></code></p> </li> <li> <p><code><code>results.push(result);</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>} else {</code></code></p> </li> <li> <p><code><code>results.push(regex.exec(text));</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> </li> <li> <p><code><code>if (results[0] == null) {</code></code></p> </li> <li> <p><code><code>resultBox.innerHTML = "匹配到0个结果";</code></code></p> </li> <li> <p><code><code>return;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> </li> <li> <p><code><code>// 倒序是有必要的</code></code></p> </li> <li> <p><code><code>for (var i = results.length - 1; i >= 0; i--) {</code></code></p> </li> <li> <p><code><code>var result = results[i];</code></code></p> </li> <li> <p><code><code>var match = result[0];</code></code></p> </li> <li> <p><code><code>var prefix = text.substr(0, result.index);</code></code></p> </li> <li> <p><code><code>var suffix = text.substr(result.index + match.length);</code></code></p> </li> <li> <p><code><code>text = prefix </code></code></p> </li> <li> <p><code><code>+ '<span class="info">'</code></code></p> </li> <li> <p><code><code>+ match</code></code></p> </li> <li> <p><code><code>+ '</span>'</code></code></p> </li> <li> <p><code><code>+ suffix;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>resultBox.innerHTML = "匹配到" + results.length + "个结果:<br>" + text;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>};</code></code></p> </li> <li> </li> <li> <p><code><code>// 生成正则表达式,核心函数</code></code></p> </li> <li> <p><code><code>function createRegex(regex) {</code></code></p> </li> <li> <p><code><code>try {</code></code></p> </li> <li> <p><code><code>if (regex[0] == "/") {</code></code></p> </li> <li> <p><code><code>regex = regex.split("/");</code></code></p> </li> <li> <p><code><code>regex.shift();</code></code></p> </li> <li> <p><code><code>var flags = regex.pop();</code></code></p> </li> <li> <p><code><code>regex = regex.join("/");</code></code></p> </li> <li> <p><code><code>regex = new RegExp(regex, flags);</code></code></p> </li> <li> <p><code><code>} else {</code></code></p> </li> <li> <p><code><code>regex = new RegExp(regex, "g");</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>return regex;</code></code></p> </li> <li> <p><code><code>} catch(e) {</code></code></p> </li> <li> <p><code><code>errBox.innerHTML = "无效的正则表达式";</code></code></p> </li> <li> <p><code><code>return false;</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>}</code></code></p> </li> <li> <p><code><code>})();</code></code></p> </li> <li> <p><code><code></script></code></code></p> </li> </ol> <h3 id="%E7%AC%AC%E4%B8%83%E7%AB%A0%E5%B0%8F%E7%BB%93">第七章小结</h3> <p>相关API的注意点,本章基本上算是一网打尽了。</p> <p>至于文中的例子,都是点睛之笔,没有详细解析。如有理解不透的,建议自己敲一敲。</p> <p> </p> <h2 id="%E5%90%8E%E8%AE%B0">后记</h2> <p>其实本文首发于:正则表达式系列总结 - 知乎专栏</p> <p>原文是一个系列。一直等到老姚成为掘金的专栏作者,经过仔细考虑,在掘金平台没有采用系列形式,而是合成为了一篇文章。这样既便于读者阅读,最起码能一气呵成地阅读。同时也便于作者统一回复留言。</p> <p>文章要结束了,最后还要有几点说明。</p> <h3 id="1.%20%E9%9C%80%E8%A6%81%E6%B3%A8%E6%84%8F%E7%9A%84%E5%9C%B0%E6%96%B9">1. 需要注意的地方</h3> <p>本文主要讨论的是JavaScript的正则表达式,更精确地说是ES5的正则表达式。</p> <p>JavaScript 的正则表达式引擎是传统型NFA的,因此本系列的讨论是适合任何一门正则引擎是传统型NFA的编程语言。当然,市面上大部分语言的正则引擎都是这种的。而 JS里正则涉及到的所有语法要点,是这种引擎支持的核心子集。也就是说,要学正则表达式,不妨以JS正则为出发点。</p> <h3 id="2.%20%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99">2. 参考资料</h3> <p>当然本文不是无本之末。主要参考的是几本书籍。</p> <p>以下书籍中核心章节都认真阅读过,甚至阅读多遍。</p> <p>《JavaScript权威指南》,看完本系列,再去看书中的第10章,你就知道了什么叫字字珠玑。</p> <p>《精通正则表达式》,权威且比较杂乱,我阅读的第一本正则表达式书籍。</p> <p>《正则表达式必知必会》,这是我看的第二本正则,看完后,确定自己算是入门了。</p> <p>《正则指引》,《精通正则表达式》的译者写的,相对清晰。</p> <p>《正则表达式入门》,我看的是英文版的,对于已经入门的我,基本没多少收获了。</p> <p>《正则表达式经典实例》,除了第3章,比较杂外,也有收获,以实例为主导的一本书。</p> <p>《JavaScript Regular Expressions》,为数不多转讲JS正则的。页数不多,也有收获。</p> <p>《高性能JavaScript 》第5章,我看的是英文版的。第5章,讲了回溯和优化。</p> <p>《JavaScript忍者秘籍》第7章,大概讲了一下正则的用法,几个例子还不错。</p> <p>《JavaScript高级程序设计》第5.4节,比较简短的介绍。</p> <p>使用的工具:</p> <p><strong>Regulex,一款可视化工具<br>ProcessOn - 免费在线作图,实时协作<br>LICEcap – 灵活好用,GIF 屏幕录制工具</strong></p> <h3 id="3.%20%E4%B8%AA%E4%BA%BA%E6%84%9F%E6%82%9F">3. 个人感悟</h3> <p><strong>要多写文章的</strong></p> <p>首 先,我十分感谢读者。读者能在信息泛滥的网络里,点击我的文章进来瞧两眼,这都是对其注意力的消费。更何况,还有很多童鞋都认真读了,甚至给我挑毛病,这 都是对我的帮助。不知有多少童鞋是从头读到这里的,不妨留言打卡,让我知道你是用心的读者,而并非简简单单地收藏一下,然后就再也不曾看过了。</p> <p>说到要写文章,其目的是以教为学。看似为了教,其实是为了学<strong>。</strong>能教会别人才算自己真正学会了,最起码形成了文字,通过了自己的语言逻辑这一关。如果还能有人指出你的错误认知,那样收获就更大了,何乐而不为呢?</p> <p>很多书中都提到类似的观点,例如《知道做到》《好好学习》《与时间做朋友》《暗时间》等。</p> <p><strong>以教为学的其他手段</strong></p> <p>当然,以教为学的手段还有很多,比如<strong>翻译一本书</strong>。我私下已经翻译了好几本(窃喜^_^)。</p> <p>可 以从薄点的书籍开始,比如100页左右的。基本上使用有道就可以了,也不用要求自己一词一句的翻译,能用自己的话说明白就行了。说到这里,不得不提起,我 们的阮一峰大神,在我看来,他就是成功地应用这种模式的。看完外文的文章,理解明白了,用自己的话说一说,再形成自己的简练风格。</p> <p>恐怕你可能说自己的英文水平不够,没信心尝试。相信我,熟悉了常用词汇(比如literal翻译成字面量)后,配合有道翻译,薄点的书,一天翻译一章是没问题的。当然前提是你懂相关领域,不然是没办法意译的。</p> <p>最后一种以教为学的手段是,<strong>写一本书</strong>。写文章是基础,文章多了,自然而言就可以写成一本书。当然,写书强调的是整体架构,所以文章最好成体系。</p> <p>你看看那些国内专业书籍的作者,一般都事先翻译过几本书的。最起码在前端领域,我就看到了好几位是这么干的。翻译明白了,学会了,用自己的角度去弄出一本书还是相对很容易的。</p> <p>虽然,本人并未曾写过书,但上述方法,我始终相信是可行的。</p> <p>最后,我们该想到,陆游诗人对前端界做出的最大贡献是:</p> <p>纸上得来终觉浅,绝知此事要躬行。</p> <p>本文完。</p> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1305752077208686592"></div> <script type="text/javascript" src="/views/front/js/chanyan.js"></script> <!-- 文章页-底部 动态广告位 --> <div class="youdao-fixed-ad" id="detail_ad_bottom"></div> </div> <div class="col-md-3"> <div class="row" id="ad"> <!-- 文章页-右侧1 动态广告位 --> <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_1"> </div> </div> <!-- 文章页-右侧2 动态广告位 --> <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_2"></div> </div> <!-- 文章页-右侧3 动态广告位 --> <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad"> <div class="youdao-fixed-ad" id="detail_ad_3"></div> </div> </div> </div> </div> </div> </div> <div class="container"> <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(JS,JS正则表达式)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1835512920797179904.htm" title="element实现动态路由+面包屑" target="_blank">element实现动态路由+面包屑</a> <span class="text-muted">软件技术NINI</span> <a class="tag" taget="_blank" href="/search/vue%E6%A1%88%E4%BE%8B/1.htm">vue案例</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>el-breadcrumb是ElementUI组件库中的一个面包屑导航组件,它用于显示当前页面的路径,帮助用户快速理解和导航到应用的各个部分。在Vue.js项目中,如果你已经安装了ElementUI,就可以很方便地使用el-breadcrumb组件。以下是一个基本的使用示例:安装ElementUI(如果你还没有安装的话):你可以通过npm或yarn来安装ElementUI。bash复制代码npmi</div> </li> <li><a href="/article/1835506236842405888.htm" title="C#中使用split分割字符串" target="_blank">C#中使用split分割字符串</a> <span class="text-muted">互联网打工人no1</span> <a class="tag" taget="_blank" href="/search/c%23/1.htm">c#</a> <div>1、用字符串分隔:usingSystem.Text.RegularExpressions;stringstr="aaajsbbbjsccc";string[]sArray=Regex.Split(str,"js",RegexOptions.IgnoreCase);foreach(stringiinsArray)Response.Write(i.ToString()+"");输出结果:aaabbbc</div> </li> <li><a href="/article/1835498547785592832.htm" title="【华为OD机试真题2023B卷 JAVA&JS】We Are A Team" target="_blank">【华为OD机试真题2023B卷 JAVA&JS】We Are A Team</a> <span class="text-muted">若博豆</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%8D%8E%E4%B8%BA/1.htm">华为</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a> <div>华为OD2023(B卷)机试题库全覆盖,刷题指南点这里WeAreATeam时间限制:1秒|内存限制:32768K|语言限制:不限题目描述:总共有n个人在机房,每个人有一个标号(1<=标号<=n),他们分成了多个团队,需要你根据收到的m条消息判定指定的两个人是否在一个团队中,具体的:1、消息构成为:abc,整数a、b分别代</div> </li> <li><a href="/article/1835497074049773568.htm" title="数组去重" target="_blank">数组去重</a> <span class="text-muted">好奇的猫猫猫</span> <div>整理自js中基础数据结构数组去重问题思考?如何去除数组中重复的项例如数组:[1,3,4,3,5]我们在做去重的时候,一开始想到的肯定是,逐个比较,外面一层循环,内层后一个与前一个一比较,如果是久不将当前这一项放进新的数组,挨个比较完之后返回一个新的去过重复的数组不好的实践方式上述方法效率极低,代码量还多,思考?有没有更好的方法这时候不禁一想当然有了!!!hashtable啊,通过对象的hash办法</div> </li> <li><a href="/article/1835494258262503424.htm" title="【JS】执行时长(100分) |思路参考+代码解析(C++)" target="_blank">【JS】执行时长(100分) |思路参考+代码解析(C++)</a> <span class="text-muted">l939035548</span> <a class="tag" taget="_blank" href="/search/JS/1.htm">JS</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a> <div>题目为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。假设GPU最多一次执行n个任务,一次执行耗时1秒,在保证GPU不空闲情况下,最少需要多长时间执行完成。题目输入第一个参数为GPU一次最多执行的任务个数,取值范围[1,10000]第二个参数为任务数组长度,取值范围[1,10000]第三个参数为任务数组,数字范围</div> </li> <li><a href="/article/1835492740536823808.htm" title="node.js学习" target="_blank">node.js学习</a> <span class="text-muted">小猿L</span> <a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a> <div>node.js学习实操及笔记温故node.js,node.js学习实操过程及笔记~node.js学习视频node.js官网node.js中文网实操笔记githubcsdn笔记为什么学node.js可以让别人访问我们编写的网页为后续的框架学习打下基础,三大框架vuereactangular离不开node.jsnode.js是什么官网:node.js是一个开源的、跨平台的运行JavaScript的运行</div> </li> <li><a href="/article/1835485429059645440.htm" title="docker" target="_blank">docker</a> <span class="text-muted">igotyback</span> <a class="tag" taget="_blank" href="/search/eureka/1.htm">eureka</a><a class="tag" taget="_blank" href="/search/%E4%BA%91%E5%8E%9F%E7%94%9F/1.htm">云原生</a> <div>Docker容器的文件系统是隔离的,但是可以通过挂载卷(Volumes)或绑定挂载(BindMounts)将宿主机的文件系统目录映射到容器内部。要查看Docker容器的映射路径,可以使用以下方法:查看容器配置:使用dockerinspect命令可以查看容器的详细配置信息,包括挂载的卷。例如:bashdockerinspect在输出的JSON格式中,查找"Mounts"部分,这里会列出所有的挂载信息</div> </li> <li><a href="/article/1835480639814594560.htm" title="在Ubuntu中编译含有JSON的文件出现报错" target="_blank">在Ubuntu中编译含有JSON的文件出现报错</a> <span class="text-muted">芝麻糊76</span> <a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/kill_bug/1.htm">kill_bug</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/ubuntu/1.htm">ubuntu</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a> <div>在ubuntu中进行JSON相关学习的时候,我发现了一些小问题,决定与大家进行分享,减少踩坑时候出现不必要的时间耗费截取部分含有JSON部分的代码进行展示char*str="{\"title\":\"JSONExample\",\"author\":{\"name\":\"JohnDoe\",\"age\":35,\"isVerified\":true},\"tags\":[\"json\",\"</div> </li> <li><a href="/article/1835478496810463232.htm" title="Xinference如何注册自定义模型" target="_blank">Xinference如何注册自定义模型</a> <span class="text-muted">玩人工智能的辣条哥</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/AI/1.htm">AI</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8B/1.htm">大模型</a><a class="tag" taget="_blank" href="/search/Xinference/1.htm">Xinference</a> <div>环境:Xinference问题描述:Xinference如何注册自定义模型解决方案:1.写个model_config.json,内容如下{"version":1,"context_length":2048,"model_name":"custom-llama-3","model_lang":["en","ch"],"model_ability":["generate","chat"],"model</div> </li> <li><a href="/article/1835455048277127168.htm" title="Python神器!WEB自动化测试集成工具 DrissionPage" target="_blank">Python神器!WEB自动化测试集成工具 DrissionPage</a> <span class="text-muted">亚丁号</span> <a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>一、前言用requests做数据采集面对要登录的网站时,要分析数据包、JS源码,构造复杂的请求,往往还要应付验证码、JS混淆、签名参数等反爬手段,门槛较高。若数据是由JS计算生成的,还须重现计算过程,体验不好,开发效率不高。使用浏览器,可以很大程度上绕过这些坑,但浏览器运行效率不高。因此,这个库设计初衷,是将它们合而为一,能够在不同须要时切换相应模式,并提供一种人性化的使用方法,提高开发和运行效率</div> </li> <li><a href="/article/1835447985601867776.htm" title="Mongodb Error: queryTxt ETIMEOUT xxxx.wwwdz.mongodb.net" target="_blank">Mongodb Error: queryTxt ETIMEOUT xxxx.wwwdz.mongodb.net</a> <span class="text-muted">佛一脚</span> <a class="tag" taget="_blank" href="/search/error/1.htm">error</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a> <div>背景每天都能遇到奇怪的问题,做个记录,以便有缘人能得到帮助!换了一台电脑开发nextjs程序。需要连接mongodb数据,对数据进行增删改查。上一台电脑好好的程序,新电脑死活连不上mongodb数据库。同一套代码,没任何修改,搞得我怀疑人生了,打开浏览器进入mongodb官网毫无问题,也能进入线上系统查看数据,网络应该是没问题。于是我尝试了一下手机热点,这次代码能正常跑起来,连接数据库了!!!是不</div> </li> <li><a href="/article/1835443569528238080.htm" title="Vue( ElementUI入门、vue-cli安装)" target="_blank">Vue( ElementUI入门、vue-cli安装)</a> <span class="text-muted">m0_l5z</span> <a class="tag" taget="_blank" href="/search/elementui/1.htm">elementui</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a> <div>一.ElementUI入门目录:1.ElementUI入门1.1ElementUI简介1.2Vue+ElementUI安装1.3开发示例2.搭建nodejs环境2.1nodejs介绍2.2npm是什么2.3nodejs环境搭建2.3.1下载2.3.2解压2.3.3配置环境变量2.3.4配置npm全局模块路径和cache默认安装位置2.3.5修改npm镜像提高下载速度2.3.6验证安装结果3.运行n</div> </li> <li><a href="/article/1835437775344726016.htm" title="博客网站制作教程" target="_blank">博客网站制作教程</a> <span class="text-muted">2401_85194651</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/maven/1.htm">maven</a> <div>首先就是技术框架:后端:Java+SpringBoot数据库:MySQL前端:Vue.js数据库连接:JPA(JavaPersistenceAPI)1.项目结构blog-app/├──backend/│├──src/main/java/com/example/blogapp/││├──BlogApplication.java││├──config/│││└──DatabaseConfig.java</div> </li> <li><a href="/article/1835435506645692416.htm" title="00. 这里整理了最全的爬虫框架(Java + Python)" target="_blank">00. 这里整理了最全的爬虫框架(Java + Python)</a> <span class="text-muted">有一只柴犬</span> <a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB%E7%B3%BB%E5%88%97/1.htm">爬虫系列</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>目录1、前言2、什么是网络爬虫3、常见的爬虫框架3.1、java框架3.1.1、WebMagic3.1.2、Jsoup3.1.3、HttpClient3.1.4、Crawler4j3.1.5、HtmlUnit3.1.6、Selenium3.2、Python框架3.2.1、Scrapy3.2.2、BeautifulSoup+Requests3.2.3、Selenium3.2.4、PyQuery3.2</div> </li> <li><a href="/article/1835431726982197248.htm" title="vue 创建项目报错:command failed: npm install --loglevel error" target="_blank">vue 创建项目报错:command failed: npm install --loglevel error</a> <span class="text-muted">那鱼、会飞</span> <a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/vue-cli3/1.htm">vue-cli3</a> <div>这个问题其实很好解决,只是很多种情况,逐一排除即可。稳下心来~vuecli3创建项目我的node版本是node14.15.0,(永远不要尝试最新版本)node各种版本下载地址:以往的版本|Node.js(nodejs.org)vue/cli@3.12.1npminstall-g@vue/cli@3.12.1(注意vue/cli2和vue/cli3的下载命名有所改变,2是-形式,3是/形式)其实报错</div> </li> <li><a href="/article/1835430719363575808.htm" title="更改npm镜像源为淘宝镜像" target="_blank">更改npm镜像源为淘宝镜像</a> <span class="text-muted">骆小骆</span> <a class="tag" taget="_blank" href="/search/%E5%9F%BA%E4%BA%8Enode.js/1.htm">基于node.js</a> <div>npm常用指令后缀*最近复习了一下node.js整理了一下跟node.js相关的指令后缀*--save、-S参数意思是把模块的版本信息保存到dependencies(生产环境依赖)中,即你的package.json文件的dependencies字段中;–--save-dev、-D参数意思是把模块版本信息保存到devDependencies(开发环境依赖)中,即你的package.json文件的de</div> </li> <li><a href="/article/1835430340806668288.htm" title="COCO 格式的数据集转化为 YOLO 格式的数据集" target="_blank">COCO 格式的数据集转化为 YOLO 格式的数据集</a> <span class="text-muted">QYQY77</span> <a class="tag" taget="_blank" href="/search/YOLO/1.htm">YOLO</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a> <div>"""--json_path输入的json文件路径--save_path保存的文件夹名字,默认为当前目录下的labels。"""importosimportjsonfromtqdmimporttqdmimportargparseparser=argparse.ArgumentParser()parser.add_argument('--json_path',default='./instances</div> </li> <li><a href="/article/1835415332345442304.htm" title="NPM私库搭建-verdaccio(Linux)" target="_blank">NPM私库搭建-verdaccio(Linux)</a> <span class="text-muted">Beam007</span> <a class="tag" taget="_blank" href="/search/npm/1.htm">npm</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a> <div>1、安装nodelinux服务器安装nodea)、官网下载所需的node版本https://nodejs.org/dist/v14.21.0/b)、解压安装包若下载的是xxx.tar.xz文件,解压命令为tar-xvfxxx.tar.xzc)、修改环境变量修改:/etc/profile文件#SETPATHFORNODEJSexportNODE_HOME=NODEJS解压安装的路径exportPAT</div> </li> <li><a href="/article/1835399831535841280.htm" title="python实现规则引擎_规则引擎python" target="_blank">python实现规则引擎_规则引擎python</a> <span class="text-muted">weixin_39601511</span> <a class="tag" taget="_blank" href="/search/python%E5%AE%9E%E7%8E%B0%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E/1.htm">python实现规则引擎</a> <div>广告关闭回望2020,你在技术之路上,有什么收获和成长么?对于未来,你有什么期待么?云+社区年度征文,各种定制好礼等你!我正在用python编写日志收集分析应用程序,我需要编写一个“规则引擎”来匹配和处理日志消息。它需要具有以下特点:正则表达式匹配消息本身消息严重性优先级的算术比较布尔运算符我设想一个例子规则可能是这样的:(message~program:messageandseverity>=h</div> </li> <li><a href="/article/1835398064727224320.htm" title="前端代码上传文件" target="_blank">前端代码上传文件</a> <span class="text-muted">余生逆风飞翔</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a> <div>点击上传文件import{ElNotification}from'element-plus'import{API_CONFIG}from'../config/index.js'import{UploadFilled}from'@element-plus/icons-vue'import{reactive}from'vue'import{BASE_URL}from'../config/index'i</div> </li> <li><a href="/article/1835397938482868224.htm" title="golang获取用户输入的几种方式" target="_blank">golang获取用户输入的几种方式</a> <span class="text-muted">余生逆风飞翔</span> <a class="tag" taget="_blank" href="/search/golang/1.htm">golang</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a> <div>一、定义结构体typeUserInfostruct{Namestring`json:"name"`Ageint`json:"age"`Addstring`json:"add"`}typeReturnDatastruct{Messagestring`json:"message"`Statusstring`json:"status"`DataUserInfo`json:"data"`}二、get请求的</div> </li> <li><a href="/article/1835393400380157952.htm" title="VUE3 + xterm + nestjs实现web远程终端 或 连接开启SSH登录的路由器和交换机。" target="_blank">VUE3 + xterm + nestjs实现web远程终端 或 连接开启SSH登录的路由器和交换机。</a> <span class="text-muted">焚木灵</span> <a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/vue/1.htm">vue</a> <div>可远程连接系统终端或开启SSH登录的路由器和交换机。相关资料:xtermjs/xterm.js:Aterminalfortheweb(github.com)后端实现(NestJS):1、安装依赖:npminstallnode-ssh@nestjs/websockets@nestjs/platform-socket.io2、我们将创建一个名为RemoteControlModule的NestJS模块,</div> </li> <li><a href="/article/1835392896531001344.htm" title="Regular Expression 正则表达式" target="_blank">Regular Expression 正则表达式</a> <span class="text-muted">Aimyon_36</span> <a class="tag" taget="_blank" href="/search/Data/1.htm">Data</a><a class="tag" taget="_blank" href="/search/Development/1.htm">Development</a><a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a> <div>RegularExpression前言1.基本匹配2.元字符2.1点运算符.2.2字符集2.2.1否定字符集2.3重复次数2.3.1*号2.3.2+号2.3.3?号2.4{}号2.5(...)特征标群2.6|或运算符2.7转码特殊字符2.8锚点2.8.1^号2.8.2$号3.简写字符集4.零宽度断言(前后预查)4.1?=...正先行断言4.2?!...负先行断言4.3?Thefatcatsaton</div> </li> <li><a href="/article/1835379153730367488.htm" title="spring mvc @RequestBody String类型参数" target="_blank">spring mvc @RequestBody String类型参数</a> <span class="text-muted">zoyation</span> <a class="tag" taget="_blank" href="/search/spring-mvc/1.htm">spring-mvc</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/mvc/1.htm">mvc</a> <div>通过如下配置:text/html;charset=UTF-8application/json;charset=UTF-8在springmvc的Controller层使用@RequestBody接收Content-Type为application/json的数据时,默认支持Map方式和对象方式参数@RequestMapping(value="/{code}/saveUser",method=Requ</div> </li> <li><a href="/article/1835376631733121024.htm" title="Vue 项目运行时,报错 Error: Cannot find module ‘node:path‘" target="_blank">Vue 项目运行时,报错 Error: Cannot find module ‘node:path‘</a> <span class="text-muted">周bro</span> <a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/node.js/1.htm">node.js</a><a class="tag" taget="_blank" href="/search/npm/1.htm">npm</a> <div>node-v是否显示nodenpm-v报错Error:Cannotfindmodule‘node:path'是因为node版本和npm版本不匹配安装相对应的版本node版本10.16.0对应npm版本6.9.0npminstallnpm@6.14.17-g执行该命令即可匹配版本官网https://nodejs.org/en/about/previous-releases</div> </li> <li><a href="/article/1835373236217540608.htm" title="360前端星计划-动画可以这么玩" target="_blank">360前端星计划-动画可以这么玩</a> <span class="text-muted">马小蜗</span> <div>动画的基本原理定时器改变对象的属性根据新的属性重新渲染动画functionupdate(context){//更新属性}constticker=newTicker();ticker.tick(update,context);动画的种类1、JavaScript动画操作DOMCanvas2、CSS动画transitionanimation3、SVG动画SMILJS动画的优缺点优点:灵活度、可控性、性能</div> </li> <li><a href="/article/1835370074102394880.htm" title="【vite 自动配置路由】" target="_blank">【vite 自动配置路由】</a> <span class="text-muted">CODER-V</span> <a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E8%BD%AF%E4%BB%B6%E6%9E%84%E5%BB%BA/1.htm">软件构建</a> <div>手动配置路由,是一个没有技术含量又浪费时间的工作。本文将介绍vite构建的vue3项目如何编写一个自动配置路由的脚本。约定大于配置要想使用脚本完成路由的自动配置,我们就需要遵循以下目录规则:每一个页面对应一个包,当前包下的主页面命名为index.vue;每个包里必须配置一个page.js;在每一个page.js里边配置,额外的路由信息,比如:exportdefault{title:'商品',men</div> </li> <li><a href="/article/1835362136054919168.htm" title="探索Zebra4J:构建高效企业级Web应用的微服务框架" target="_blank">探索Zebra4J:构建高效企业级Web应用的微服务框架</a> <span class="text-muted">叶准鑫Natalie</span> <div>探索Zebra4J:构建高效企业级Web应用的微服务框架ZebraZebra4J/Zebra4Js基于SpringBoot的JavaWeb/Nodejs框架项目地址:https://gitcode.com/gh_mirrors/zebra/Zebra项目介绍在当今快速发展的技术环境中,构建高效、可扩展的企业级Web应用是每个开发团队的追求。Zebra4J作为一款基于SpringBoot的全新微服务</div> </li> <li><a href="/article/1835359727924637696.htm" title="Nginx从入门到实践(三)" target="_blank">Nginx从入门到实践(三)</a> <span class="text-muted">听你讲故事啊</span> <div>动静分离动静分离是将网站静态资源(JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问。动静分离的一种做法是将静态资源部署在nginx上,后台项目部署到应用服务器上,根据一定规则静态资源的请求全部请求nginx服务器,达到动静分离的目标。rewrite规则Rewrite规则常见正则表达式Rewrite主要的功能就是实现URL的重写,Ngin</div> </li> <li><a href="/article/1835333268375957504.htm" title="爬虫技术抓取网站数据" target="_blank">爬虫技术抓取网站数据</a> <span class="text-muted">Bearjumpingcandy</span> <a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/1.htm">爬虫</a> <div>爬虫技术是一种自动化获取网站数据的技术,它可以模拟人类浏览器的行为,访问网页并提取所需的信息。以下是爬虫技术抓取网站数据的一般步骤:发起HTTP请求:爬虫首先会发送HTTP请求到目标网站,获取网页的内容。解析HTML:获取到网页内容后,爬虫会使用HTML解析器解析HTML代码,提取出需要的数据。数据提取:通过使用XPath、CSS选择器或正则表达式等工具,爬虫可以从HTML中提取出所需的数据,如文</div> </li> <li><a href="/article/20.htm" title="矩阵求逆(JAVA)初等行变换" target="_blank">矩阵求逆(JAVA)初等行变换</a> <span class="text-muted">qiuwanchi</span> <a class="tag" taget="_blank" href="/search/%E7%9F%A9%E9%98%B5%E6%B1%82%E9%80%86%EF%BC%88JAVA%EF%BC%89/1.htm">矩阵求逆(JAVA)</a> <div>package gaodai.matrix; import gaodai.determinant.DeterminantCalculation; import java.util.ArrayList; import java.util.List; import java.util.Scanner; /** * 矩阵求逆(初等行变换) * @author 邱万迟 *</div> </li> <li><a href="/article/147.htm" title="JDK timer" target="_blank">JDK timer</a> <span class="text-muted">antlove</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jdk/1.htm">jdk</a><a class="tag" taget="_blank" href="/search/schedule/1.htm">schedule</a><a class="tag" taget="_blank" href="/search/code/1.htm">code</a><a class="tag" taget="_blank" href="/search/timer/1.htm">timer</a> <div>1.java.util.Timer.schedule(TimerTask task, long delay):多长时间(毫秒)后执行任务 2.java.util.Timer.schedule(TimerTask task, Date time):设定某个时间执行任务 3.java.util.Timer.schedule(TimerTask task, long delay,longperiod</div> </li> <li><a href="/article/274.htm" title="JVM调优总结 -Xms -Xmx -Xmn -Xss" target="_blank">JVM调优总结 -Xms -Xmx -Xmn -Xss</a> <span class="text-muted">coder_xpf</span> <a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">应用服务器</a> <div>堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。 典型设置: java -Xmx</div> </li> <li><a href="/article/401.htm" title="JDBC连接数据库" target="_blank">JDBC连接数据库</a> <span class="text-muted">Array_06</span> <a class="tag" taget="_blank" href="/search/jdbc/1.htm">jdbc</a> <div>package Util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCUtil { //完</div> </li> <li><a href="/article/528.htm" title="Unsupported major.minor version 51.0(jdk版本错误)" target="_blank">Unsupported major.minor version 51.0(jdk版本错误)</a> <span class="text-muted">oloz</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div>java.lang.UnsupportedClassVersionError: cn/support/cache/CacheType : Unsupported major.minor version 51.0 (unable to load class cn.support.cache.CacheType) at org.apache.catalina.loader.WebappClassL</div> </li> <li><a href="/article/655.htm" title="用多个线程处理1个List集合" target="_blank">用多个线程处理1个List集合</a> <span class="text-muted">362217990</span> <a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a><a class="tag" taget="_blank" href="/search/thread/1.htm">thread</a><a class="tag" taget="_blank" href="/search/list/1.htm">list</a><a class="tag" taget="_blank" href="/search/%E9%9B%86%E5%90%88/1.htm">集合</a> <div>  昨天发了一个提问,启动5个线程将一个List中的内容,然后将5个线程的内容拼接起来,由于时间比较急迫,自己就写了一个Demo,希望对菜鸟有参考意义。。 import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; public c</div> </li> <li><a href="/article/782.htm" title="JSP简单访问数据库" target="_blank">JSP简单访问数据库</a> <span class="text-muted">香水浓</span> <a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/jsp/1.htm">jsp</a> <div>学习使用javaBean,代码很烂,仅为留个脚印 public class DBHelper { private String driverName; private String url; private String user; private String password; private Connection connection; privat</div> </li> <li><a href="/article/909.htm" title="Flex4中使用组件添加柱状图、饼状图等图表" target="_blank">Flex4中使用组件添加柱状图、饼状图等图表</a> <span class="text-muted">AdyZhang</span> <a class="tag" taget="_blank" href="/search/Flex/1.htm">Flex</a> <div>1.添加一个最简单的柱状图 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?xml version= "1.0"&n</div> </li> <li><a href="/article/1036.htm" title="Android 5.0 - ProgressBar 进度条无法展示到按钮的前面" target="_blank">Android 5.0 - ProgressBar 进度条无法展示到按钮的前面</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a> <div>在低于SDK < 21 的版本中,ProgressBar 可以展示到按钮前面,并且为之在按钮的中间,但是切换到android 5.0后进度条ProgressBar 展示顺序变化了,按钮再前面,ProgressBar 在后面了我的xml配置文件如下:   [html]  view plain copy   <RelativeLa</div> </li> <li><a href="/article/1163.htm" title="查询汇总的sql" target="_blank">查询汇总的sql</a> <span class="text-muted">baalwolf</span> <a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a> <div>select   list.listname, list.createtime,listcount from dream_list as list ,   (select listid,count(listid) as listcount  from dream_list_user  group by listid  order by count(</div> </li> <li><a href="/article/1290.htm" title="Linux du命令和df命令区别" target="_blank">Linux du命令和df命令区别</a> <span class="text-muted">BigBird2012</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a> <div>        1,两者区别             du,disk usage,是通过搜索文件来计算每个文件的大小然后累加,du能看到的文件只是一些当前存在的,没有被删除的。他计算的大小就是当前他认为存在的所有文件大小的累加和。        </div> </li> <li><a href="/article/1417.htm" title="AngularJS中的$apply,用还是不用?" target="_blank">AngularJS中的$apply,用还是不用?</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/AngularJS/1.htm">AngularJS</a><a class="tag" taget="_blank" href="/search/%24apply/1.htm">$apply</a> <div>        在AngularJS开发中,何时应该调用$scope.$apply(),何时不应该调用。下面我们透彻地解释这个问题。         但是首先,让我们把$apply转换成一种简化的形式。         scope.$apply就像一个懒惰的工人。它需要按照命</div> </li> <li><a href="/article/1544.htm" title="[Zookeeper学习笔记十]Zookeeper源代码分析之ClientCnxn数据序列化和反序列化" target="_blank">[Zookeeper学习笔记十]Zookeeper源代码分析之ClientCnxn数据序列化和反序列化</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</a> <div>ClientCnxn是Zookeeper客户端和Zookeeper服务器端进行通信和事件通知处理的主要类,它内部包含两个类,1. SendThread 2. EventThread, SendThread负责客户端和服务器端的数据通信,也包括事件信息的传输,EventThread主要在客户端回调注册的Watchers进行通知处理   ClientCnxn构造方法   &</div> </li> <li><a href="/article/1671.htm" title="【Java命令一】jmap" target="_blank">【Java命令一】jmap</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/Java%E5%91%BD%E4%BB%A4/1.htm">Java命令</a> <div>jmap命令的用法:   [hadoop@hadoop sbin]$ jmap Usage: jmap [option] <pid> (to connect to running process) jmap [option] <executable <core> (to connect to a </div> </li> <li><a href="/article/1798.htm" title="Apache 服务器安全防护及实战" target="_blank">Apache 服务器安全防护及实战</a> <span class="text-muted">ronin47</span> <div>此文转自IBM. Apache 服务简介 Web 服务器也称为 WWW 服务器或 HTTP 服务器 (HTTP Server),它是 Internet 上最常见也是使用最频繁的服务器之一,Web 服务器能够为用户提供网页浏览、论坛访问等等服务。 由于用户在通过 Web 浏览器访问信息资源的过程中,无须再关心一些技术性的细节,而且界面非常友好,因而 Web 在 Internet 上一推出就得到</div> </li> <li><a href="/article/1925.htm" title="unity 3d实例化位置出现布置?" target="_blank">unity 3d实例化位置出现布置?</a> <span class="text-muted">brotherlamp</span> <a class="tag" taget="_blank" href="/search/unity%E6%95%99%E7%A8%8B/1.htm">unity教程</a><a class="tag" taget="_blank" href="/search/unity/1.htm">unity</a><a class="tag" taget="_blank" href="/search/unity%E8%B5%84%E6%96%99/1.htm">unity资料</a><a class="tag" taget="_blank" href="/search/unity%E8%A7%86%E9%A2%91/1.htm">unity视频</a><a class="tag" taget="_blank" href="/search/unity%E8%87%AA%E5%AD%A6/1.htm">unity自学</a> <div>问:unity 3d实例化位置出现布置? 答:实例化的同时就可以指定被实例化的物体的位置,即 position  Instantiate (original : Object, position : Vector3, rotation : Quaternion) : Object 这样你不需要再用Transform.Position了,   如果你省略了第二个参数(</div> </li> <li><a href="/article/2052.htm" title="《重构,改善现有代码的设计》第八章 Duplicate Observed Data" target="_blank">《重构,改善现有代码的设计》第八章 Duplicate Observed Data</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E9%87%8D%E6%9E%84/1.htm">重构</a> <div> import java.awt.Color; import java.awt.Container; import java.awt.FlowLayout; import java.awt.Label; import java.awt.TextField; import java.awt.event.FocusAdapter; import java.awt.event.FocusE</div> </li> <li><a href="/article/2179.htm" title="struts2更改struts.xml配置目录" target="_blank">struts2更改struts.xml配置目录</a> <span class="text-muted">chiangfai</span> <a class="tag" taget="_blank" href="/search/struts.xml/1.htm">struts.xml</a> <div>struts2默认是读取classes目录下的配置文件,要更改配置文件目录,比如放在WEB-INF下,路径应该写成../struts.xml(非/WEB-INF/struts.xml) web.xml文件修改如下:   <filter> <filter-name>struts2</filter-name> <filter-class&g</div> </li> <li><a href="/article/2306.htm" title="redis做缓存时的一点优化" target="_blank">redis做缓存时的一点优化</a> <span class="text-muted">chenchao051</span> <a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/hadoop/1.htm">hadoop</a><a class="tag" taget="_blank" href="/search/pipeline/1.htm">pipeline</a> <div>        最近集群上有个job,其中需要短时间内频繁访问缓存,大概7亿多次。我这边的缓存是使用redis来做的,问题就来了。       首先,redis中存的是普通kv,没有考虑使用hash等解结构,那么以为着这个job需要访问7亿多次redis,导致效率低,且出现很多redi</div> </li> <li><a href="/article/2433.htm" title="mysql导出数据不输出标题行" target="_blank">mysql导出数据不输出标题行</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%AF%BC%E5%87%BA/1.htm">数据导出</a><a class="tag" taget="_blank" href="/search/%E5%8E%BB%E6%8E%89%E7%AC%AC%E4%B8%80%E8%A1%8C/1.htm">去掉第一行</a><a class="tag" taget="_blank" href="/search/%E5%8E%BB%E6%8E%89%E6%A0%87%E9%A2%98/1.htm">去掉标题</a> <div>当想使用数据库中的某些数据,想将其导入到文件中,而想去掉第一行的标题是可以加上-N参数 如通过下面命令导出数据: mysql -uuserName -ppasswd -hhost -Pport -Ddatabase -e " select * from tableName"  > exportResult.txt 结果为: studentid</div> </li> <li><a href="/article/2560.htm" title="phpexcel导出excel表简单入门示例" target="_blank">phpexcel导出excel表简单入门示例</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a><a class="tag" taget="_blank" href="/search/Excel/1.htm">Excel</a><a class="tag" taget="_blank" href="/search/phpexcel/1.htm">phpexcel</a> <div>先下载PHPEXCEL类文件,放在class目录下面,然后新建一个index.php文件,内容如下 <?php error_reporting(E_ALL); ini_set('display_errors', TRUE); ini_set('display_startup_errors', TRUE);   if (PHP_SAPI == 'cli') die('</div> </li> <li><a href="/article/2687.htm" title="爱情格言" target="_blank">爱情格言</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/%E6%A0%BC%E8%A8%80/1.htm">格言</a> <div> 1) I love you not because of who you are, but because of who I am when I am with you.    我爱你,不是因为你是一个怎样的人,而是因为我喜欢与你在一起时的感觉。   2) No man or woman is worth your tears, and the one who is, won‘t</div> </li> <li><a href="/article/2814.htm" title="转 Activity 详解——Activity文档翻译" target="_blank">转 Activity 详解——Activity文档翻译</a> <span class="text-muted">e200702084</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/UI/1.htm">UI</a><a class="tag" taget="_blank" href="/search/sqlite/1.htm">sqlite</a><a class="tag" taget="_blank" href="/search/%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86/1.htm">配置管理</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%BA%94%E7%94%A8/1.htm">网络应用</a> <div>activity 展现在用户面前的经常是全屏窗口,你也可以将 activity 作为浮动窗口来使用(使用设置了 windowIsFloating 的主题),或者嵌入到其他的 activity (使用 ActivityGroup )中。 当用户离开 activity 时你可以在 onPause() 进行相应的操作 。更重要的是,用户做的任何改变都应该在该点上提交 ( 经常提交到 ContentPro</div> </li> <li><a href="/article/2941.htm" title="win7安装MongoDB服务" target="_blank">win7安装MongoDB服务</a> <span class="text-muted">geeksun</span> <a class="tag" taget="_blank" href="/search/mongodb/1.htm">mongodb</a> <div>1.  下载MongoDB的windows版本:mongodb-win32-x86_64-2008plus-ssl-3.0.4.zip,Linux版本也在这里下载,下载地址: http://www.mongodb.org/downloads   2.  解压MongoDB在D:\server\mongodb, 在D:\server\mongodb下创建d</div> </li> <li><a href="/article/3068.htm" title="Javascript魔法方法:__defineGetter__,__defineSetter__" target="_blank">Javascript魔法方法:__defineGetter__,__defineSetter__</a> <span class="text-muted">hongtoushizi</span> <a class="tag" taget="_blank" href="/search/js/1.htm">js</a> <div>转载自: http://www.blackglory.me/javascript-magic-method-definegetter-definesetter/ 在javascript的类中,可以用defineGetter和defineSetter_控制成员变量的Get和Set行为 例如,在一个图书类中,我们自动为Book加上书名符号: function Book(name){ </div> </li> <li><a href="/article/3195.htm" title="错误的日期格式可能导致走nginx proxy cache时不能进行304响应" target="_blank">错误的日期格式可能导致走nginx proxy cache时不能进行304响应</a> <span class="text-muted">jinnianshilongnian</span> <a class="tag" taget="_blank" href="/search/cache/1.htm">cache</a> <div>昨天在整合某些系统的nginx配置时,出现了当使用nginx cache时无法返回304响应的情况,出问题的响应头: Content-Type:text/html; charset=gb2312 Date:Mon, 05 Jan 2015 01:58:05 GMT Expires:Mon , 05 Jan 15 02:03:00 GMT Last-Modified:Mon, 05</div> </li> <li><a href="/article/3322.htm" title="数据源架构模式之行数据入口" target="_blank">数据源架构模式之行数据入口</a> <span class="text-muted">home198979</span> <a class="tag" taget="_blank" href="/search/PHP/1.htm">PHP</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a><a class="tag" taget="_blank" href="/search/%E8%A1%8C%E6%95%B0%E6%8D%AE%E5%85%A5%E5%8F%A3/1.htm">行数据入口</a> <div>注:看不懂的请勿踩,此文章非针对java,java爱好者可直接略过。   一、概念 行数据入口(Row Data Gateway):充当数据源中单条记录入口的对象,每行一个实例。   二、简单实现行数据入口 为了方便理解,还是先简单实现: <?php /** * 行数据入口类 */ class OrderGateway { /*定义元数</div> </li> <li><a href="/article/3449.htm" title="Linux各个目录的作用及内容" target="_blank">Linux各个目录的作用及内容</a> <span class="text-muted">pda158</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E8%84%9A%E6%9C%AC/1.htm">脚本</a> <div>1)根目录“/”   根目录位于目录结构的最顶层,用斜线(/)表示,类似于 Windows 操作系统的“C:\“,包含Fedora操作系统中所有的目录和文件。   2)/bin   /bin   目录又称为二进制目录,包含了那些供系统管理员和普通用户使用的重要 linux命令的二进制映像。该目录存放的内容包括各种可执行文件,还有某些可执行文件的符号连接。常用的命令有:cp、d</div> </li> <li><a href="/article/3576.htm" title="ubuntu12.04上编译openjdk7" target="_blank">ubuntu12.04上编译openjdk7</a> <span class="text-muted">ol_beta</span> <a class="tag" taget="_blank" href="/search/HotSpot/1.htm">HotSpot</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/jdk/1.htm">jdk</a><a class="tag" taget="_blank" href="/search/OpenJDK/1.htm">OpenJDK</a> <div>获取源码 从openjdk代码仓库获取(比较慢) 安装mercurial Mercurial是一个版本管理工具。 sudo apt-get install mercurial 将以下内容添加到$HOME/.hgrc文件中,如果没有则自己创建一个: [extensions] forest=/home/lichengwu/hgforest-crew/forest.py fe</div> </li> <li><a href="/article/3703.htm" title="将数据库字段转换成设计文档所需的字段" target="_blank">将数据库字段转换成设计文档所需的字段</a> <span class="text-muted">vipbooks</span> <a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a><a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C/1.htm">工作</a><a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a> <div>        哈哈,出差这么久终于回来了,回家的感觉真好!         PowerDesigner的物理数据库一出来,设计文档中要改的字段就多得不计其数,如果要把PowerDesigner中的字段一个个Copy到设计文档中,那将会是一件非常痛苦的事情。</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>