正则表达式是来匹配一个字符串的。"Regular Expression" 这个词太长了,咱们通常应用它的缩写 "regex" 或者 "regexp"。 正则表达式能够被用来替换字符串中的文本、验证表单、基于模式匹配从一个字符串中提取字符串等等。

从当初开始,辞别copy正则表达式!

在咱们编码过程中,正则表达式是咱们常来顾客,尤其是表单字段的校验。为了图不便,经常的做法就是去网上进货,而后作为中间商卖给表单。这种做法尽管不便,然而只能满足一般客户(表单)的需要,如果遇到一个大客户须要定制产品(个性的校验规定),到时咱们再去学习如何制作就来不及了,客户不等人,失去了客户不说(我的项目延期),有可能还有扣你尾款(挨下级批斗);

一、基础知识

根本语法

/pattern/[modifiers];
  • pattern:模式
  • modifiers:修饰符

修饰符

修饰符 能够在全局搜寻中不辨别大小写:

修饰符形容
i执行对大小写不敏感的匹配。
g执行全局匹配(查找所有匹配而非在找到第一个匹配后进行)。
m执行多行匹配。

示例

var str = 'aBc Abcd abcde';str.match(/bcd/);         // ["bcd"]str.match(/bcd/g);        // ["bcd", "bcd"]str.match(/abc/g);        // ["abc"]str.match(/abc/gi);       // ["aBc", "Abc", "abc"]

咱们在应用过程中,大多数状况都是须要全局匹配的,大小写是否敏感须要依据理论状况来看。

当同时应用多个描述符时,形容的程序无要求:

str.match(/abc/gi);       // ["aBc", "Abc", "abc"]str.match(/abc/ig);       // ["aBc", "Abc", "abc"]

形容字符

依据正则表达式语法规定,大部分字符仅可能形容本身,这些字符被称为一般字符,如所有的字母、数字等。

元字符就是领有动静性能的特殊字符,须要加反斜杠进行标识,以便与一般字符和转义字符进行区别,JavaScript 正则表达式反对的元字符如表所示。

元字符形容
.查找单个字符,除了换行和行结束符
\w查找单词字符
\W查找非单词字符
\d查找数字
\D查找非数字字符
\s查找空白字符
\S查找非空白字符
\b匹配单词边界
\B匹配非单词边界
\n查找换行符
\f查找换页符
\r查找回车符
\t查找制表符
\v查找垂直制表符
\xxx查找以八进制数 xxxx 规定的字符
\xdd查找以十六进制数 dd 规定的字符
\uxxxx查找以十六进制 xxxx规定的 Unicode 字符

示例

var str = "hello word 12a3";str.match(/./gi);           // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "d", " ", "1", "2", "a", "3"]str.match(/\d/gi);          // ["1", "2", "3"]str.match(/\D/gi);          // ["h", "e", "l", "l", "o", " ", "w", "o", "r", "d", " ", "a"]str.match(/\w/gi);          // ["h", "e", "l", "l", "o", "w", "o", "r", "d", "1", "2", "a", "3"]str.match(/\W/gi);          // [" ", " "]str.match(/\s/gi);          // [" ", " "]str.match(/\S/gi);          // ["h", "e", "l", "l", "o", "w", "o", "r", "d", "1", "2", "a", "3"]str.match(/\b/gi);          // ["", "", "", "", "", ""]str.match(/\B/gi);          // ["", "", "", "", "", "", "", "", "", ""]var str = '你好世界! Hello word!'// 匹配任意 ASCII 字符str.match(/[\u0000-\u00ff]/g);  // ["!", " ", "H", "e", "l", "l", "o", " ", "w", "o", "r", "d", "!"]// 匹配任意双字节的汉字str.match(/[^\u0000-\u00ff]/g);   // ["你", "好", "世", "界"]// 匹配大写字母str.match( /[\u0041-\u004A]/g);  // ["H"]

反复匹配

能够对于某个内容进行屡次匹配

量词形容
n+匹配任何蕴含至多一个 n 的字符串
n*匹配任何蕴含零个或多个 n 的字符串
n?匹配任何蕴含零个或一个 n 的字符串
n{x}匹配蕴含 x 个 n 的序列的字符串
n{x,y}匹配蕴含起码 x 个、最多 y 个 n 的序列的字符串
n{x,}匹配蕴含至多 x 个 n 的字符串

示例

var str = 'Hello helllo hehello hehehelllloooo'str.match(/he/gi);     // ["He", "He", "He", "he", "He", "he", "he", "he"]str.match(/(he)+/gi);  // ["He", "He", "Hehe", "Hehehehe"]str.match(/(he)*/gi);  // ["He", "", "", "", "", "He", "", "", "", "", "", "Hehe", "", "", "", "", "Hehehehe", "", "", "", "", "", "", "", "", ""]str.match(/(he)?/gi);  // ["He", "", "", "", "", "He", "", "", "", "", "", "He", "he", "", "", "", "", "He", "he", "he", "he", "", "", "", "", "", "", "", "", ""]str.match(/(he){1}/gi); // ["He", "He", "He", "he", "He", "he", "he", "he"]str.match(/(he){2}/gi); // ["Hehe", "Hehe", "hehe"]str.match(/(he){2,}/gi); //  ["Hehe", "Hehehehe"]str.match(/(he){3,4}/gi); // ["Hehehehe"]str.match(/(he)+l+/gi);   // ["Hell", "Helll", "Hehell", "Hehehehellll"]str.match(/(he)+l{3,}/gi);   //  ["Helll", "Hehehehellll"]

通过下面的例子,咱们能够发现几个不同的用法能够失去雷同的后果:

  • n+ 等同于 n{1,}
  • n? 等同于 n{0,1}
  • n* 等同于 n{0,}

边界量词

匹配模式的地位

量词形容
^匹配结尾,在多行检测中,会匹配一行的结尾
$匹配结尾,在多行检测中,会匹配一行的结尾

示例

var str = 'abc ABC';/^abc/gi.exec(str);    // ['abc']/abc$/gi.exec(str);    // ['ABC'] /abc/gi.exec(str);     // ['abc']

如果不增加^和$,则默认从结尾匹配

匹配范畴

表达式形容
[abc]查找方括号之间的任何字符。
[0-9]查找任何从 0 至 9 的数字。
(x\y)查找任何以\分隔的选项。即x或y

示例

var str = 'Hello RegExp 369'str.match(/[2-8]/gi);     // ["3", "6"]str.match(/[el]/gi);      // ["e", "l", "l", "e", "E"]str.match(/[x|5|6]/gi);   // ["x", "6"]str.match(/[a-h]/gi);     // ["H", "e", "e", "g", "E"]// 你也能够多个一起应用str.match(/[2-8a-h]/gi);  // ["H", "e", "e", "g", "E", "3", "6"]

转义字符

通过下面的学习咱们能够看到,在正则表达式中,通过应用一些特殊字符,能够示意不同的匹配模式。如:+、{}、^、? 等。那么当咱们须要匹配这些特殊字符怎么办呢?比如说:匹配‘1 + 2 = 3’ 中的 "+",此时咱们就须要对“+”进行本义,即在须要本义的字符后面加上“\”。

var str = '1 + 1 = 3'str.match(/\+/gi);    // ["+"]

如果只须要匹配一个“+”,当你不进行本义时会报错:

当咱们须要匹配以下特殊字符时,咱们须要进行本义:

$()*+.[]?\^{}|

二、断言

假如有这样一个场景,须要在"今日18:00-20:00全场5折,洗衣液只有¥19,不要错过哦"中匹配出价格。

价格是由数字组成,如果咱们只通过数字匹配的话,会把其余信息也匹配进去:

var str = '今日18:00-20:00全场5折,洗衣液只有¥19,不要错过哦';str.match(/\d+/gi);       // ["18", "00", "20", "00", "5", "19"]

显然只通过数字是不行的,能够留神到,在¥符号前面的才是价格,其余的都不是,如果有什么办法能够匹配指定内容的前面就好了。答案就是断言:

var str = '今日18:00-20:00全场5折,洗衣液只有¥19,不要错过哦';str.match(/(?<=¥)\d+/gi);       // ["19"]

断言分为4种

符号形容含意
reg(?=exp)正向后行断言匹配reg,且前面内容满足exp
reg(?!exp)负向后行断言匹配reg,且前面内容不满足exp
(?<=exp)reg正向后发断言匹配reg,且后面内容满足exp
(?<!exp)reg负向后发断言匹配reg,且后面内容不满足exp

正向后行断言

形如 A(?=B) 的模式,示意匹配到A,且A的前面是B的内容。

var str = 'I scream, you scream, we all scream for ice-cream!'// 匹配scream后面的一个单词str.match(/\w+(?=\sscream)/gi);   // ["I", "you", "all"]

负向后行断言

形如 A(?!B) 的模式,示意匹配到A,且A后面的内容不能满足B。

var str = 'I scream, you scream, we all scream for ice-cream!';// 匹配scream单词,且前面不能是空格,str.match(/scream(?!\s)/gi);     // ["scream", "scream"]   只能匹配到第一和第二个

正向后发断言

形如 (?<=B)A 的模式,示意匹配到A,且A的后面满足B。

var str = 'I scream, you scream, we all scream for ice-cream!?'// 匹配scream后的单词str.match(/(?<=scream\s)\w+/gi);   ["for"]

负向后发断言

形如 (?<!B)A 的模式,示意匹配到A,且A后面的不满足B。

var str = 'I scream, you scream, we all scream for ice-cream!';// 匹配cream,且后面不能为字母str.match(/(?<!\w)cream/gi);    // ["cream"]   只能匹配到ice-cream中的cream

可能很多人看了之后很容易把这几个记忆混同,这里教大家一个简略的办法了解与记忆:

  • 断言(exp)写在前面就是匹配前面的内容,写在后面就是匹配后面的内容
  • 正向示意满足该条件(符号 = ),负向示意不满足该条件(符号 ! )
欢送拜访我的集体网站(置信你会喜爱上我的格调):www.dengzhanyong.com
关注我的集体公众号【前端筱园】,不错过我的每一篇推送

三、罕用的正则表达式

  • 正整数: ^\d+$
  • 负整数: ^-\d+$
  • 电话号码: ^+?[\d\s]{3,}$
  • 电话代码: ^+?[\d\s]+(?[\d\s]{10,}$
  • 整数: ^-?\d+$
  • 用户名: ^[\w\d_.]{4,16}$
  • 字母数字字符: ^[a-zA-Z0-9]*$
  • 带空格的字母数字字符: ^[a-zA-Z0-9 ]*$
  • 明码: ^(?=^.{6,}$)((?=.*[A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z]))^.*$
  • 电子邮件: ^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})*$
  • IPv4 地址: ^((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))*$
  • 小写字母: ^([a-z])*$
  • 大写字母: ^([A-Z])*$
  • 网址: ^(((http|https|ftp):\/\/)?([[a-zA-Z0-9]\-\.])+(\.)([[a-zA-Z0-9]]){2,4}([[a-zA-Z0-9]\/+=%&_\.~?\-]*))*$
  • VISA 信用卡号码: ^(4[0-9]{12}(?:[0-9]{3})?)*$
  • 日期 (MM/DD/YYYY): ^(0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.](19|20)?[0-9]{2}$
  • 日期 (YYYY/MM/DD): ^(19|20)?[0-9]{2}[- /.](0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])$

......

四、正则表达式办法

test()

test() 办法用于检测一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false。

exec()

exec() 办法用于检索字符串中的正则表达式的匹配。该函数返回一个数组,其中寄存匹配的后果。如果未找到匹配,则返回值为 null。

其余办法应用正则表达式

match()

match() 办法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。

replace()

replace() 办法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

示例

// 将所有的数字替换为*var text = 'aaa126bbb34278ccc23';text.replace(/\d/gi, '*');   // "aaa***bbb*****ccc**"

search()

search() 办法应用表达式来搜寻匹配,而后返回匹配的地位。

示例

// 获取至多呈现两个间断数字的地位var text = 'ab1cfd3ff452de7532';text.search(/\d{2,}/gi);   // 9

五、如何写出高效的正则表达式

  1. 误匹配

    对于 +*? 这几个符号须要依据理论场景抉择适合的应用,不要把他们混同

  2. 漏匹配

    如须要匹配18位的身份证号,如果这样写 \d{18} 就会呈现漏匹配的状况,因为身份证的最初一位可能是 X ,能够这样改良:\d{17}(X|x|\d)

  3. 明确

    通常越简略的正则匹配到的后果就越多,还是拿身份证号来举例,18个0也满足下面的匹配的条件,然而这很显著不是一个省份证号。为了失去更加精确的匹配后果,这就须要要求咱们的正则更加明确。

六、实战演练

咱们来写一个匹配身份证号码的正则,首先须要理解身份证号码的构造。在很久前我写过一篇文章【你晓得身份证是如何防伪的吗?】,这里我就不具体解说了。

地址码长度为6,第一位1-9,后5位0-9

/^[1-9]\d{5}/

年份码长度为4,前两位可能是18、19、20,后两位都是0-9

/(18|19|20)\d{2}/

月份码两位01-12,日期码2位01-31

/((0[1-9])|1[0-2])(([0-2][1-9])|10|20|30|31)/

程序码是3位0-9的数字

/\d{3}/

校验码1位可能是0-9或者X,X也可能是小写x

/\d{17}(X|\d|x)$/ 

也能够这样写

/\d{17}[0-9Xx]$/

最初把他们组合起来

/^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/

通过这个正则能够判断是否合乎身份证号码的根本要求,然而如果须要更加准确的校验的话,就须要通过编写一些计划来进行校验了。如各省级的地址码为:

华北:北京11,天津12,河北13,山西14,内蒙古15

西南: 辽宁21,吉林22,黑龙江23

华东: 上海31,江苏32,浙江33,安徽34,福建35,江西36,山东37

华中: 河南41,湖北42,湖南43

华南: 广东44,广西45,海南46

东北: 四川51,贵州52,云南53,西藏54,重庆50

东南: 陕西61,甘肃62,青海63,宁夏64,新疆65

特地:台湾71,香港81,澳门82

有些月份没有31号,校验码是否正确等等.....

欢送拜访我的集体网站(置信你会喜爱上我的格调):www.dengzhanyong.com
关注我的集体公众号【前端筱园】,不错过我的每一篇推送