跳到主要内容

JS 正则表达式(JavaScript regular expression)

· 阅读需 23 分钟
givebest

RegExp 直接量和对象的创建

就像字符串和数字一样,程序中每个取值相同的原始类型直接量均表示相同的值,这是显而易见的。程序运行时每次遇到对象直接量(初始化表达式)诸如{}和[]的时候都会创建新对象。比如,如果在循环体中写 var a = [],则每次遍历都会创建一个新的空数组。正则表达式直接量则与此不同,ECMAScript 3 规范规定,一个正则表达式直接量会在执行到它时转换为一个 RegExp 对象,同一段代码所表示正则表达式直接量的每次运算都返回同一个对象。ECMAScript 5 规范则做了相反的规定,同一段代码所表示的正则表达式直接量的每次运算都返回新对象。I E 一直都是按照 EC-MAScript 5 规范实现的,多数最新版本的浏览器也开始遵循 EC-MAScript 5,尽管目前该标准并未全面广泛推行。

1.1 直接量字符

正则表达式中的所有字母和数字都是按照字面含义进行匹配的。JavaScript 正则表达式语法也支持非字母的字符匹配,这些字符需要通过反斜线(\)作为前缀进行转义。比如,转义字符\n 用以匹配换行符。表 10-1 中列出了这些转义字符。

表10-1:正则表达式中的直接量字符
字符匹配
字母和数字字符自身
\oNUL字符(\u0000)
\t制表符(\u0009)
\n换行符(\u000A)
\v垂直制表符(\u000B)
\f换页符(\u000C)
\r回车符(\u000D)
\xnn由十六进制数nn指定的拉丁字符,例如,\x0A等价于\n
\uxxxx由十六进制数xxxx指定的Unicode字符,例如\u0009等价于\t
\cX控制字符^X,例如,\cJ等价于换行符\n
在正则表达式中,许多标点符号具有特殊含义,它们是:^ $ . * + ? = ! : | \ / ( ) [ ] {}

正则表达式“/\/”用以匹配任何包含反斜线的字符串。

1.2 字符类

例如,/[\u0400-\u04FF]/用以匹配所有的 Cyrillic 字符)。

表10-2:正则表达式的字符类
字符匹配
[...]方括号内的任意字符
[^...]不在方括号内的任意字符
.除换行符和其他Unicode行终止符之外的任意字符
\w任何ASCII字符组成的单词,等价于[a-zA-Z0-9]
\W任何不是ASCII字符组成的单词,等价于[^a-zA-Z0-9]
\s任何Unicode空白符
\S任何非Unicode空白符的字符,注意\w和\S不同
\d任何ASCII数字,等价于[0-9]
\D除了ASCII数字之外的任何字符,等价于[^0-9]
\b退格直接量(特例)

注意,在方括号之内也可以写这些特殊转义字符。比如,由于\s 匹配所有的空白字符,\d 匹配的是所有数字,因此/[\s\d]/就匹配任意空白符或者数字。注意,这里有一个特例。下面我们将会看到转义符\b 具有的特殊含义,当用在字符类中时,它表示的是退格符,所以要在正则表达式中按照直接量表示一个退格符,只需要使用具有一个元素的字符类/[\b]/。

1.3 重复

我们在正则模式之后跟随用以指定字符重复的标记。由于某些重复种类非常常用,因此就有一些专门用于表示这种情况的特殊字符。例如,“+”用以匹配前一个模式的一个或多个副本。表 10-3 总结了这些表示重复的正则语法。

表10-3:正则表达式的重复字符语法
字符含义
{n,m }匹配前一项至少n次,但不能超过m次
{n, }匹配前一项至少n次
{n }匹配前一顶n次
?匹配前一项0次或者1次,也就是说前一项是可选的,等价于{0,1}
+匹配前一项1次或多次,等价于{1,}
*匹配前一项0次或多次,等价于{0,}

这里有一些例子:

/\d{2,4}/ // 匹配 2 ~ 4 个数字
/\w{3}\d?/ // 精确匹配三个单词和一个可选的数字
/\s+java\s+/ // 匹配前后带有一个或多个空格的字符串"java"
/[^(]_/ // 匹配一个或多个非左括号的字符

在使用“_”和“?”时要注意,由于这些字符可能匹配 0 个字符,因此它们允许什么都不匹配。例如,正则表达式/a*/实际上与字符串“bbbb”匹配,因为这个字符串含有 0 个 a。

非贪婪的重复

表 10-3 中列出的匹配重复字符是尽可能多地匹配,而且允许后续的正则表达式继续匹配。因此,我们称之为“贪婪的”匹配。我们同样可以使用正则表达式进行非贪婪匹配。只须在待匹配的字符后跟随一个问号即可:“??”、“+?”、“*?”或{1,5}?。比如,正则表达式/a+/可以匹配一个或多个连续的字母 a。当使用“aaa”作为匹配字符串时,正则表达式会匹配它的三个字符。但是/a+?/也可以匹配一个或多个连续字母 a,但它是尽可能少地匹配。我们同样将“aaa”作为匹配字符串,但后一个模式只能匹配第一个 a。

使用非贪婪的匹配模式所得到的结果可能和期望并不一致。考虑以下正则表达式/a+b/,它可以匹配一个或多个 a,以及一个 b。当使用“aaab”作为匹配字符串时,它会匹配整个字符串。现在再试一下非贪婪匹配的版本/a+?b/,它匹配尽可能少的 a 和一个 b。当用它来匹配“aaab”时,你期望它能匹配一个 a 和最后一个 b。但实际上,这个模式却匹配了整个字符串,和该模式的贪婪匹配一模一样。这是因为正则表达式的模式匹配总是会寻找字符串中第一个可能匹配的位置。由于该匹配是从字符串的第一个字符开始的,因此在这里不考虑它的子串中更短的匹配。

1.4 选择、分组和引用

正则表达式的语法还包括指定选择项、子表达式分组和引用前一子表达式的特殊字符。字符“|”用于分隔供选择的字符。例如,/ab|cd|ef/可以匹配字符串“ab”,也可以匹配字符串“cd”,还可以匹配字符串“ef”。/\d{3}|[a-z]{4}/匹配的是三位数字或者四个小写字母。 注意,选择项的尝试匹配次序是从左到右,直到发现了匹配项。如果左边的选择项匹配,就忽略右边的匹配项,即使它产生更好的匹配。因此,当正则表达式/a|ab/匹配字符串“ab”时,它只能匹配第一个字符。

表10-4:正则表达式的选择、分组和引用字符
字符含义
|选择,匹配的是该符号左边的子表达式或右边的子表达式
(...)组合,将几个项组合为一个单元,这个单元可通过“*”、“+”、“?”和“|”等符号加以修饰,而且可以记住和这个组合相匹配的字符串以供此后的引用使用
(?:...)只组合,把项组合到一个单元,但不记忆与该组相匹配的字符
\n和第n个分组第一次匹配的字符相匹配,组是圆括号中的子表达式(也有可能是嵌套的),组索引是从左到右的左括号数,“(?:”形式的分组不编码

1.5 指定匹配位置

正如前面所介绍的,正则表达式中的多个元素才能够匹配字符串的一个字符。例如,\s 匹配的只是一个空白符。还有一些正则表达式的元素匹配的是字符之间的位置,而不是实际的字符。 最常用的锚元素是^,它用来匹配字符串的开始,锚元素$用以匹配字符串的结束。

表10-5:正则表达式中的锚字符
字符含义
^匹配字符串的开头,在多行检索中,匹配一行的开头
$匹配字符串的结尾,在多行检索中,匹配一行的结尾
\b匹配一个单词的边界,简言之,就是位于字符\w和\W之间的位置,或位于字符\w和字符串的开头或者结尾之间的位置(但需要注意,[\b]匹配的是退格符)
\B匹配非单词边界的位置
(?=p)零宽正向先行断言,要求接下来的字符都与p匹配,但不能包括匹配p的那些字符
(?!p)零宽负向先行断言,要求接下来的字符不与p匹配

1.6 修饰符

正则表达式中的语法还有最后一个知识点,即正则表达式的修饰符,用以说明高级匹配模式的规则。和之前讨论的正则表达式语法不同,修饰符是放在“/”符号之外的,也就是说,它们不是出现在两条斜线之间,而是第二条斜线之后。JavaScript 支持三个修饰符,修饰符“i”用以说明模式匹配是不区分大小写的。修饰符“g”说明模式匹配应该是全局的,也就是说,应该找出被检索字符串中所有的匹配。修饰符“m”用以在多行模式中执行匹配,在这种模式下,如果待检索的字符串包含多行,那么^和$锚字符除了匹配整个字符串的开始和结尾之外,还能匹配每行的开始和结尾。比如正则表达式/java$/im 可以匹配“java”也可以匹配“Java\nis fun”。

表10-6:正则表达式修饰符
字符含义
i执行不区分大小写的匹配
g执行一个全局匹配,简言之,即找到所有的匹配,而不是在找到第一个之后就停止
m多行匹配模式,^匹配一行的开头和字符串的开头,$匹配行的结束和字符串的结束

2.用于模式匹配的 String 方法

方法描述FFIE
search检索与正则表达式相匹配的值。14
match找到一个或多个正则表达式的匹配。14
replace替换与正则表达式匹配的子串。14
split把字符串分割为字符串数组。14

它的参数是一个正则表达式,返回第一个与之匹配的子串的起始位置,如果找不到匹配的子串,它将返回-1。 如果 search() 的参数不是正则表达式,则首先会通过 RegExp 构造函数将它转换成正则表达式,search()方法不支持全局检索,因为它忽略正则表达式参数中的修饰符 g。

"JavaScript".search(/script/i); //4

2.2 replace()

replace()方法用以执行检索与替换操作。其中第一个参数是一个正则表达式,第二个参数是要进行替换的字符串。

"JavaScript".replace(/javascript/gi, "a"); //"a"
// 一段引用文本起始于引号,结束于引号 // 中间的内容区域不能包含引号
var quote = /"([^"]*)"/g;
// 用中文半角引号替换英文引号,同时要保持引号之间的内容(存储在$1中)没有被修改
text.replace(quote, "“$1”");

2.3 match()

match() 方法是最常用的 String 正则表达式方法。它的唯一参数就是一个正则表达式(或通过 RegExp()构造函数将其转换为正则表达式),返回的是一个由匹配结果组成的数组。

"1 plus 2 equals 3".match(/\d+/g) // 返回 ["1", "2", "3"]
例如,使用如下的代码来解析一个URL
var url = /(\w+):\/\/([\w.]+)\/(\S*)/;
var text = "Visit my blog at http://www.example.com/~david";
var result = text.match(url);
if (result != null) {
var fullurl = result[0];
// 包含 "http://www.example.com/~david"
var protocol = result[1];
// 包含 "http"
var host = result[2];
// 包含 "www.example.com"
var path = result[3];
// 包含 "~david"
}

2.4 split()

这个方法用以将调用它的字符串拆分为一个子串组成的数组,使用的分隔符是 split() 的参数

"123,456,789".split(","); // 返回 ["123","456","789"]

split() 方法的参数也可以是一个正则表达式,这使得 split() 方法异常强大。例如,可以指定分隔符,允许两边可以留有任意多的空白符:

"1, 2, 3, 4, 5".split(/\s*,\s*/); // 返回 ["1","2","3","4","5"]

3.RegExp 对象

正则表达式是通过 RegExp 对象来表示的。除了 RegExp() 构造函数之外,RegExp 对象还支持三个方法和一些属性。 RegExp()构造函数带有两个字符串参数,其中第二个参数是可选的,RegExp() 用以创建新的 RegExp 对象。第一个参数包含正则表达式的主体部分,也就是正则表达式直接量中两条斜线之间的文本。需要注意的是,不论是字符串直接量还是正则表达式,都使用“\”字符作为转义字符的前缀,因此当给 RegExp() 传入一个字符串表述的正则表达式时,必须将“\”替换成“\”。RegExp() 的第二个参数是可选的,如果提供第二个参数,它就指定正则表达式的修饰符。不过只能传入修饰符 g、i、m 或者它们的组合。比如:

// 全局匹配字符串中的5个数字,注意这里使用了"\\",而不是"\"var zipcode = new RegExp("\\d{5}", "g");

3.1 RegExp 的属性

每个 RegExp 对象都包含 5 个属性。属性 source 是一个只读的字符串,包含正则表达式的文本。属性 global 是一个只读的布尔值,用以说明这个正则表达式是否带有修饰符 g。属性 ignore-Case 也是一个只读的布尔值,用以说明正则表达式是否带有修饰符 i。属性 multiline 是一个只读的布尔值,用以说明正则表达式是否带有修饰符 m。最后一个属性 lastIndex,它是一个可读/写的整数。如果匹配模式带有 g 修饰符,这个属性存储在整个字符串中下一次检索的开始位置,这个属性会被 exec()test()方法用到,下面会讲到。

属性描述FFIE
globalRegExp 对象是否具有标志 g。14
ignoreCaseRegExp 对象是否具有标志 i。14
lastIndex一个整数,标示开始下一次匹配的字符位置。14
multilineRegExp 对象是否具有标志 m。14
source正则表达式的源文本。14

3.2 RegExp 的方法

RegExp 对象定义了两个用于执行模式匹配操作的方法。它们的行为和上文介绍过的 String 方法很类似。RegExp 最主要的执行模式匹配的方法是 exec(),它与 10.2 节介绍过的 String 方法 match()相似,只是 RegExp 方法的参数是一个字符串,而 String 方法的参数是一个 RegExp 对象。

方法描述FFIE
compile编译正则表达式。14
exec检索字符串中指定的值。返回找到的值,并确定其位置。14
test检索字符串中指定的值。返回 true 或 false。14

3.2.1exec()

var pattern = /Java/g;
var text = "JavaScript is more fun than Java!";
var result;
while ((result = pattern.exec(text)) != null) {
alert("Matched '" +
result[0] + "'" +
" at position " + result.index + ";
next search begins at " + pattern.lastIndex);
}

3.2.2test()

另外一个 RegExp 方法是 test(),它比 exec()更简单一些。它的参数是一个字符串,用 test()对某个字符串进行检测,如果包含正则表达式的一个匹配结果,则返回 true:

var pattern = /java/i;
pattern.test("JavaScript"); // 返回 true

4.常用正则表达式

电话号码

/^([\+][0-9]{1,3}([ \.\-])?)?([\(][0-9]{1,6}[\)])?([0-9 \.\-]{1,32})(([A-Za-z \:]{1,11})?[0-9]{1,4}?)$/;

邮箱

/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i;

日期(YYYY-MM-DD)

/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/;

IPV4

/^((([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))[.]){3}(([0-1]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))$/;

URL

/^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;

数字(允许 +-123,123.123)

/^[\-\+]?((([0-9]{1,3})([,][0-9]{3})*)|([0-9]+))?([\.]([0-9]+))?$/;

2-20 个英文或中文字符

/^([\u4e00-\u9fa5]{2,20})$|^([a-zA-Z]{2,20})$

更多常用可访问:https://github.com/posabsolute/jQuery-Validation-Engine/blob/master/js/languages/jquery.validationEngine-zh_CN.js

来源&参考

转载请注明出处: https://blog.givebest.cn/javascript/2015/11/18/javascript-regular-expression.html