正则表达式

  正则表达式的出现主要是为了更好的处理字符问题,C、C++直接支持正则表达式,一般利用库实现,Javascript处理字符串不使用正则表达式也可以, 引入正则表达式的目的就是提高字符串的处理速度,浏览器的执行效率更高,同时程序员编程更方便。

  在总结正则表达式的用法之前,先通过一个案例,为了体现正则表达式的简洁性,用下面的代码实现前面一节利用数组实现的字符查找替换 《字符串替换》

        
8        //把字符串str里面的错别字“核”替换为正确的“和”
9        let str1 = "热爱核平,核平世界。";
10       //创建一个正则表达式对象
11       let re = new RegExp("核","g");
12       //调用字符串对象replace
13       let str2 = str1.replace(re,"和")
14       document.write(str2);
    

代码解析

  代码相比原来的要简洁的多,代码虽然简单,但是背后的指点比较多,这里以小见大,先整体解释一下。第11行代码定义了一个正则表达式对象, 它的作用是规定如何查找字符串,查找什么内容,第13行代码是调用字符串对象的方法replace,把正则表达式对象作为参数,执行替换操作。

  RegExp()是正则表达式构造函数,既然是构造函数,创建对象就需要new运算符。这里我在论述的时候强调RegExp是构造函数,目的就是为了当你了解一个抽象的概念, 在学习一个新的具象知识,要学会把具象的知识归类到你已经掌握的抽象概念里面。一提到构造函数,你就应该想到new操作符和对象。RegExp()构造函数的第一个参数是 正则表达式的模式,简单说就是你查找的内容,正则表达式的模式有非常多的形式,本案例中代码构造函数RegExp()的第一个参数是字符串"核",表示查找"核"这个字符。 构造函数RegExp()的第二各参数是正则表达式的属性,简单说就是查找规则,上面代码的参数"g"表示把字符串中的全部字符"核"都找出来,而不是找出来第一个就不找了。

  replace()是字符串对象的方法,自然调用他的格式就是“字符串对象名.replace()”,该方法的作用就是替换字符,第一个参数是正则表达式对象,约束如何查,查什么, 第二个参数就是用于替换的字符串。执行该方法后原来的字符串不受影响,会重新返回一个字符串赋值给你所声明的变量。

直接量语法(正则表达式对象简化写法)

  格式: /pattern/attributes(/模式/属性)

  实际编程的过程中,创建数组对象,大家一般使用中括号符号[],而不是构造函数Array(),对于正则表达式也一样,由于经常使用,可以用简单的符号表示。 new RegExp('核','g')可以使用/核/g来代替, 两者完全是等价的,第一个斜杠后面对应的是正则表达式的模式,也就是构造函数RegExp() 的第一个参数,第二个斜杠后面对应的是正则表示的属性,也就是构造函数RegExp()的第二个参数。

替换后的代码精简为:

    let str1 = "热爱核平,核平世界。";
    let str2 = str1.replace(/核/g,"和");
    document.write(str2);

字符串方法

  有了前面的实际代码作为支撑,在进行总结就不会感到空洞。字符串对象的部分方法 在《String对象》 这节课已经总结过,与正则表达式相关的方法没有进行总结,放在本节课为大家列举。

search()

  查找与表示方法参数的字符或表示方法参数的正则表达式相同的字符,返回字符在字符串中的下标位置,没有搜索到返回-1。

  let str = "There is only one China in the world";
  let n = str.search(/only/);//返回结果n=9

  该方法忽略全局约束,只要检索到相同的字符就会停下来,不再继续,没有必要写g

match()

  match()检索并返回与正则表达式符合的匹配的字符。

let str = "There is only one China in the world";
  //[a-h]表示26个英文字母中的a~h之间包括a和h的所有字符,返回结果是:
let str2 = str.match(/[a-h]/g);
//返回结果是所有符合条件的字符串组成的数组["h","e","e","e","h","a","h","e","d"]

  正则表达式中的[a-h]正是正则表达式比单纯字符串表达能力强的体现,正则表达式的符号很丰富,这只是冰山一角。如果一一列举a到h的所有字母比较麻烦,但使用符号“-”来表达之间的意思, 书写方便,阅读方便。

split()

  split()作用是分割字符串,返回分割后的字符串数组集合

let str = "道,可道也,非恒道也;名,可名也,非恒名也。";
let str2 = str.split(/[,;。]/g);//[,;。]表示字符串只要出现逗号、分号、句号均作为分隔标志
//返回的分割结果["道","可道也","非恒道也","名","可名也","非恒名也"]

  这里的中括号和上面一样,因为逗号、分号、句号只有三个符号,所以分别穷举列出要查找的符号,一般这种情况适用于无规律多个字符。

字符串方法列表

  str表示字符串,Reg表示正则表达式对象,支持正则表达式的参数也支持字符串,只是字符串的表达能力有限。

方法 功能 参数
replace() 替换字符串 replace(Reg,str)
search() 查找字符,返回位置 search(Reg)
match() 查找字符,返回字符 search(Reg)
split() 分割字符串 search(Reg)

修饰符

  前面的代码演示过全局属性标志符号g,使用直接量语法时属性值位于第二个斜杠后面“/模式/属性”

修饰符 功能
g 全局搜索
i 不区分大小写搜索
m 多行搜索

正则表达式语法(RegExp对象模式)

  学习正则表达式之前你首先要有对“一个字符”有概念, 一个英文字母、一个汉字、一个阿拉伯数字都是一个独立的字符,每一个字符都有一个对应的编码格式, 汉字有多少个,就有多少个对应的编码,可能不常用的没有,英文字母区分大小写时有52个编码,世界上的语言 都有各自对应的编码,只是象形文字的多而已。

  在总结之前先用多个案例来引入

1、最简单的正则

  写什么搜什么,和字符串的表达一样,本课中的第一个案例就是

/道 从字符串中查找“道”这个字
/循道 从字符串中查找“循道”这个词
/a 从字符串中查找字母a
/hello 从字符串中查找单词hello
let str = "你好,道长,欢迎来到循道科技!Hello, priest, welcome to the Follow Dao Science!";
document.write(str.match(/道/g));
document.write("<br>");
document.write(str.match(/循道/g));
document.write("<br>");
document.write(str.match(/a/g));
document.write("<br>");
document.write(str.match(/hello/gi));//g表示全局,i表示不区分大小写,参见上面“修饰符”

2、竖线符号“|”

  对比1,如果希望搜索的不是“循道”这个词,而是“循”和“道”两个字符,简单说就是两个词分别带入查找

正则模式 含义
/循|道 分别查找字符“循”、“道”
let str = "你好,道长,欢迎来到循道科技!Hello, priest, welcome to the Follow Dao Science!";
document.write(str.match(/循|道/g));

3、中括号“[]”

  对比2,如果想查找的不是hello,合适字母h、e、l、l、o,模仿2的格式应该是“h|e|l|l|o”, 五个字母还不算麻烦,如果几十个就比较麻烦,这时候可以引入符号中括号[], ([hello]与h|e|l|l|o等价)

[]符号 |符号 含义
/[hello] /h|e|l|l|o 分别查找字符h、e、l、l、o
let str = "你好,道长,欢迎来到循道科技!Hello, priest, welcome to the Follow Dao Science!";
document.write(str.match(/[hello]/g));

4、复合使用

  在正则表达式中一个中括号和其他字符自由组合成一个长字符串的时候相当于一个字符

let str = "每个朝代的词汇影响力,周:天子、天下、中国、王道、中原" +
    "汉:汉字、汉服、汉语、汉人,唐:唐人街,宋明元清民国共和国......";
document.write(str.match(/汉[字服语人]/g));//返回结果["汉字","汉服","汉语","汉人"] 

  中括号里面的字符分别抽出来一个和外面的字符组成一个词语。

5、在[]中使用的-和^

  中括号中的字符-表示连续的意思,比如希望搜索所有的英文小写字母, 在中括号中列举26个字母比较麻烦,可以简化写法为[a-z]

连字符-案例表

表达式 意义 等价写法
[0-9] 数字0到9 [0123456789]
[0-5] 数字0到5 [012345]
[a-f] 前6个英文字母(小写) [abcdef]
[a-z] 26个英文小写字母 [abcde...xyz]
[A-Z] 26个英文大写字母 [ABCD...XYZ]
[a-zA-Z] 所有的英文字母
[a-zA-Z0-9] 所有的英文字母和数字
[a-zA-Z0-9_] 所有的英文字母、数字和下划线

^案例表

  ^符号使用在中括号里面,表示否的意思,比如[^abc], 检索字符串里面的每一个字符进行判断比较,只要字符不是a、b、c中其一,都会被选中。 ^不在中括号[]中使用的时候,不在表示不在表示否定的意思,之所以提醒这一点,有些符号 会因为所处的环境不同表示的意思有变化,联系一下编程中的转义字符就会明白。

表达式 意义
[^0-9] 非数字字符,比如一个英文字母,一个汉字
[^a-zA-Z] 非英文字母
[^天道华] 除了天、道、华三个字符以外的字符
[a-zA-Z0-9_] 所有的英文字母、数字和下划线

5、大括号{}

  中括号是为了穷举一系列的字符,大括号是为了定义一个正则表达式字符串定义里面某一个字母出现的次数

表达式 意义 等价写法
/10{2}86 /0{2}表示两个0,可以理解为缩写 /10086
/站起来{5} /来{5}表示5个“来”字符 /站起来来来来来
/站起来{1,4} 来有1到5,包括1和4 /站起来|站起来来|站起来来来|站起来来来来
/站起来{2,} 来的数量大于2个都可以 /站起来来|站起来来来|站起来来来来|...
{*} 量词*表示大于等于0 {0,}
{+} 量词+表示大于等于1 {1,}
{?} ?表示0或1,或说大于等于0小于等于1 {0,1}

号码案例

  下面的情景是你要查找一个5位数的专用电话号码

let str = "10086南海短信:你好,国际友人,欢迎来到中国黄岩岛,短信来自中国移动10086";
document.write(str.match(/[0-9]{5}/g));//查找一个连续的5位数字

6、行开始符号^h和行结束符号$

  开始结束符号表达的意思是整个字符串复合正则表达式,而不是字符串包含某个连续的字符串片段。

let str1 = "10086南海短信:你好,国际友人,欢迎来到中国黄岩岛,短信来自中国移动10086";
let str2 = "10086";
document.write(str1.match(/^[0-9]{5}$/g));//返回的结果是null,str1虽然包含10086,但整体不是10086
document.write("<br>");
document.write(str2.match(/^[0-9]{5}$/g));//返回10086
document.write("<br>");

  开始结束符号的约束条件是整个字符串,也就是说一般不用于整篇文章查找,而是判断一个字符串是否合法, 比如注册账号的时候,填写的邮箱格式不对、填写的电话号码格式不对,会有警告。

7、转义字符

  只要你有编程的经历,对转义字符应该有认识,转义字符出现一般有两个目的, 一种情况是某个符号被使用表达其他的意思,比如上面的美元$符号,表示字符串结束的意思,如果你想在字符串中检索美元符号, 就需要转义符号,反斜杠“\”,注意与斜杠"/"的区别,斜杠被用来写正则表达式,同样若果你想在字符串中全局搜索斜线,就应该 使用“/\//g” 这种表示方法,红色的斜线是正则表达式直接量语法格式,蓝色的反斜杠和蓝色的斜杠组成的转义字符结构才表示斜杠。

  转义字符还有一个用途就是为了表达某个功能,比如使用“\d”表示数字,它的等价写法是“[0-9]”,正则表达式里面并没用点(.)符号执行类似中括号、大括号的功能, 所以可以用点不写反斜杠,来代表一类符号,实际上.符号代表的就是非换行或行结束符以外的所有单个字符。点符号本身用来表示其它一个大类的符号,那么当你想在字符串中仅仅搜索 点这一个符号的时候就需要使用反斜杠转义模式“\.”来表示点本身。

部分元字符列表

  一些转义字符不列举,比如中括号、大括号、美元符号都被用来表示特定意义,如果你想表达他们只需要前置反斜杠符号“\[”、“\$”、“\{”。

字符 意义 等价写法
\d 阿拉伯数字 [0-9]
\D 非阿拉伯数字 [^0-9]
\w 英文字母,数字,下划线 [a-zA-Z0-9_]
\W 英文字母,数字,下划线以外的字符 [^a-zA-Z0-9_]
\s 空白字符,就是空格 比如\s{3}表示三个空格
\W 非空白字符

RegExp对象属性和方法

  平时书写正则表达式的时候,一般不使用构造函数RegExp(),而是使用直接量语法(/pattern/attributes)格式,会忽略正则表达式的对象特性。 比如判断一个正则对象是否具有全局搜索“g”约束,可以调用RegExp对象的属性global,设置了g标志,global的属性值是true,未设置,global的属性值是false。 访问属性自然使用点符号RegExp.global,返回结果true或false。

let reg = /^[0-9]{5}$/g;
document.write(reg.global);//返回结果true
document.write("<br>");
let reg2 = /^[0-9]{5}$/i;
document.write(reg2.global);//返回结果false
document.write("<br>");
document.write(reg2.ignoreCase);//返回结果true

global、ignoreCase、multiline属性表格

属性 含义 属性值 对应标志 标志意义
global 是否定义g true/false g 全局搜索
ignoreCase 是否定义m true/false m 多行搜索

source属性

  source属性的属性值就是正则表达式 /pattern/attributes的模式pattern部分, 不包括正则表达式直接量使用的定界符,也不包括标志 g、i、m。

let reg = /^[0-9]{5}$/g;
document.write(reg.source);//返回结果^[0-9]{5}$

  返回的结果以字符串的类型存在。typeof reg.source测试结果是string。

RegExp对象方法

  RegExp对象方法有些类似字符串的方法,不完全一样,可以对照学习。

test()

  test()方法的作用是检测字符串是否有复合正则的额字符串,方法的参数是要检测的字符串,调用方法的对象是正则对象。代码案例如下:

let str = "10086南海短信:你好,国际友人,欢迎来到中国黄岩岛,短信来自中国移动10086";
let reg = /[0-9]{5}/g;//查找字符串中是否含有5个连续的数字
document.write(reg.test(str));//reg.test(str)返回结果是true

exec()

  exec()方法和test()方法使用语法是一样的,只是返回的结果有区别,这个方法比较类似字符串的方法match(),一般不使用正则表达式的方法, 都是使用字符串对象的方法,把正则对象作为参数,使用正则对象的方法是把字符串当作对象。

let str = "你好,国际友人,欢迎来到中国黄岩岛,短信来自中国移动10086";
let reg = /[0-9]{5}/g;//查找字符串中是否含有5个连续的数字
document.write(reg.exec(str));//reg.exec(str)返回结果是10086