本资料仅提供以下语言版本:English。请 帮助我们 将其翻译为 简体中文 版本。

字符集合

考虑一个实际的任务 —— 我们有一个电话号码 "+7(903)-123-45-67",并且我们需要在这个字符串中找到所有的数字。其他的字符我们都不关心。

字符类是一种特殊符号,它匹配集合中的任何符号。

举个例子,有一个 “数字” 类。它写作 \d。我们把它放进模式里,可以在搜索过程中匹配任何数字。

例如,正则表达式 /\d/ 搜索单个数字:

let str = "+7(903)-123-45-67";

let reg = /\d/;

alert( str.match(reg) ); // 7

上面的正则表达式例子没有指定全局属性,所以它只会搜索返回第一个匹配。

我们添加一个 g 标记来搜索所有数字:

let str = "+7(903)-123-45-67";

let reg = /\d/g;

alert( str.match(reg) ); // 匹配出的数组:7,9,0,3,1,2,3,4,5,6,7

最常使用的集合:\d \s \w

刚才是数字的字符类。也有其他的字符类。

最常使用的有:

\d(“d” 来源于 “digit”)
一个数字:09 的一个字符。
\s(“s” 来源于 “space”)
一个空格符:包括空格,制表符和换行符。
\w(“w” 来源于 “word”)
一个单字字符:英语字母表中的一个字母或者一个数字或一个下划线。非英语字母(像西里尔字母或者印地语)不包含在 \w 里面。

举例来说,\d\s\w 是指一个数字后面跟着一个空格字符然后跟着一个语义字符,如 "1 Z"

一个正则表达式可以同时包含普通字符和字符类。

举个例子,CSS\d 匹配一个含有 CSS 且后面跟着一个数字的字符串。

let str = "CSS4 is cool";
let reg = /CSS\d/

alert( str.match(reg) ); // CSS4

我们也可以使用多个字符类。

alert( "I love HTML5!".match(/\s\w\w\w\w\d/) ); // 'HTML5'

该匹配为(每个字符类匹配出一个相应的结果字符):

单词边界:\b

单词边界 \b —— 是一个特殊的字符类。

它表示的不是一个字符,而是字符间的边界。

举个例子,\bJava\b 能在字符串 Hello, Java! 中匹配上 Java,但不能在 Hello, JavaScript! 里匹配上。

alert( "Hello, Java!".match(/\bJava\b/) ); // Java
alert( "Hello, JavaScript!".match(/\bJava\b/) ); // null

某种意义上来说,边界具有“零宽度”,通常一个字符类表示结果中的一个字符(如一个字或一个数字),但在这个例子中不是。

边界是一个检测。

当正则表达式引擎在做搜索时,它会沿着字符串向前尝试找到匹配内容。在字符串的每个位置它(搜索引擎)都会尝试做匹配。

当一个模式中包含 \b,它检测字符串中的位置是否满足下面条件之一:

  • 字符串开头,并且第一个字符串字符是 \w
  • 字符串结尾,并且最后一个字符串字符是 \w
  • 字符串内部:一边是 \w,另一边 —— 不是 \w

举个例子,在字符串 Hello, Java! 中下面这些位置符合 \b 规则:

所以它将匹配 \bHello\b\bJava\b,但不匹配 \bHell\b(因为 l 后面没有字边界)也不匹配 Java!\b(因为感叹号标识不是一个“字”字符,所以它后面也没有字边界)。

alert( "Hello, Java!".match(/\bHello\b/) ); // Hello
alert( "Hello, Java!".match(/\bJava\b/) );  // Java
alert( "Hello, Java!".match(/\bHell\b/) );  // null
alert( "Hello, Java!".match(/\bJava!\b/) ); // null

我们再一次查看发现 \b 会触发搜索引擎检查边界,所以 Java\b 仅在跟有一个字边界时找到 Java,并且不会在结果中添加一个字母。

通常我们使用 \b 寻找一个独立的英文字符。所以如果我们想要得到 "Java" 语言那么 \bJava\b 可以准确找到一个独立的单词,但作为"JavaScript" 的一部分时则会被忽略。

另一个例子:一个正则 \b\d\d\b 寻找单独的两位数字。换而言之,它要求在 \d\d 前后必须是一个不同于 \w 的标识(或者在字符串开头或结尾)。

alert( "1 23 456 78".match(/\b\d\d\b/g) ); // 23,78
字符边界不适用于非英文字母

字符边界校验 \b 用来检测 \w 和其他东西之间的一个边界。因为 \w 意味着一个英文单词(或一个数字或者一个下划线),所以这个检测对其他字符(像斯拉夫字母或者象形文字)不起作用。

反义集合

对于每一个字符类来说都存在其对应的“反义类”,用同样的单词表示,只不过是大写的。

“反义”意味着它匹配所有其他字符,举例来说:

\D
非数字:除了 \d 的任何字符,例如一个字母。
\S
非空格:除了 \s 的任何字符,例如一个字母。
\W
非“字”字符:除了 \w 的任何东西。
\B
非边界:\b 的反向检测。

在章节的开头我们看到如何从电话 +7(903)-123-45-67 中获取所有数字。让我们从字符串中获取一个“纯” 电话号码。

let str = "+7(903)-123-45-67";

alert( str.match(/\d/g).join('') ); // 79031234567

另一个可选的方法是寻找所有非数字并且把他们从字符串中移除:

let str = "+7(903)-123-45-67";

alert( str.replace(/\D/g, "") ); // 79031234567

空格是正则字符

请注意正则表达式可以包含空格。他们被看作正则字符。

通常我们不怎么注意空格。对于我们来说字符串 1-51 - 5 是几乎一样的。

但如果正则表达式不把空格纳入考量,它就不能工作了。

让我们尝试查找被一个连接符分割的数字。

alert( "1 - 5".match(/\d-\d/) ); // null,不匹配!

我们在正则中加入空格修复它:

alert( "1 - 5".match(/\d - \d/) ); // 1 - 5,现在生效了

当然,只有在需要匹配空格的时候才会用到它。多余的空格(就像任何多余的字符)会阻止匹配:

alert( "1-5".match(/\d - \d/) ); // null, 因为字符串 1-5 没有空格

换而言之,正则表达式中所有字符都是有效的。空格也是。

一个点号是任意字符

点号 "." 是一个特殊的字符类,可以匹配换行符外的任意字符

举个例子:

alert( "Z".match(/./) ); // Z

或者在正则的中间:

let reg = /CS.4/;

alert( "CSS4".match(reg) ); // CSS4
alert( "CS-4".match(reg) ); // CS-4
alert( "CS 4".match(reg) ); // CS 4(空格也是一个字符)

请注意点号意味着“任何字符”,但不是“不存在的一个字符”。必须要有一个可以匹配的字符:

alert( "CS4".match(/CS.4/) ); // null,无匹配因为没有可供点号匹配的字符(译者注:“.”实际匹配了数字 “4”,正则最后的一个 “4” 才是无匹配项)

总结

我们谈及的字符集合有:

  • \d — 数字。
  • \D — 非数字。
  • \s — 空格标识,制表符,换行符。
  • \S — 除了 \s 的所有。
  • \w — 英文单词,数字,下划线 '_'
  • \W — 除了 \w 的所有。
  • '.' — 除了一个换行符的任何字符。

如果我们想要检索一个像反斜杠或者一个点这样有特殊意义的字符,那么我们需要使用一个反斜杠 \. 进行转义。

请注意一个正则也可以包含像换行符 \n 这样特殊字符的字符串。这和字符类是没有冲突的,因为字符类是由其他字母组成的。

任务

时间的格式是:hours:minutes。小时和分钟都是两位数,如 09:00

编写正则表达式在字符串 Breakfast at 09:00 in the room 123:456. 中查找时间。

P.S. 在这个任务里没有必要校验时间的正确性,所以 25:99 也可算做有效的结果。 P.P.S. 正则表达式不能匹配 123:456

答案是:\b\d\d:\d\d\b

alert( "Breakfast at 09:00 in the room 123:456.".match( /\b\d\d:\d\d\b/ ) ); // 09:00
教程路线图

评论

在评论之前先阅读本内容…
  • 欢迎你在文章下添加补充内容、提出你的问题或回答提出的问题。
  • 使用 <code> 标签插入几行代码,对于多行代码 — 可以使用 <pre>,对于超过十行的代码 — 建议使用沙箱(plnkrJSBincodepen 等)。
  • 如果你无法理解文章中的内容 — 请详细说明。