【源码学习】正则表达式
模式(Patterns)和修饰符(flags)
正则表达式是提供了一种在文本中进行搜索和替换的强大的方式的模式。
在 JavaScript 中,我们可以通过 RegExp 对象使用它们,也可以与字符串方法结合使用。
正则表达式
正则表达式(可叫作 “regexp”,或 “reg”)包扩 模式 和可选的 修饰符。
有两种创建正则表达式对象的语法。
较长一点的语法:
regexp = new RegExp("pattern", "flags");
较短一点的语法,使用斜线 "/"
:
regexp = /pattern/; // 没有修饰符
regexp = /pattern/gmi; // 带有修饰符 g、m 和 i(后面会讲到)
斜线 pattern:/.../
告诉 JavaScript 我们正在创建一个正则表达式。它的作用与字符串引号的作用相同。
在这两种情况下,regexp
都会成为内建类 RegExp
的一个实例。
这两种语法之间的主要区别在于,使用斜线 /.../
的模式不允许插入表达式(如带有 ${...}
的字符串模板)。它是完全静态的。
在我们写代码时就知道正则表达式时则会使用斜线的方式 —— 这是最常见的情况。当我们需要从动态生成的字符串“动态”创建正则表达式时,更经常使用 new RegExp
。例如:
let tag = prompt("What tag do you want to find?", "h2");
let regexp = new RegExp(`<${tag}>`); // 如果在上方输入到 prompt 中的答案是 "h2",则与 /<h2>/ 相同
修饰符
正则表达式可能会有的会影响搜索结果的修饰符。
在 JavaScript 中,有 6 个修饰符:
-
pattern:i
-
使用此修饰符后,搜索时不区分大小写:
A
和a
之间没有区别(请参见下面的示例)。 -
pattern:g
- 使用此修饰符后,搜索时会寻找所有的匹配项 —— 没有它,则仅返回第一个匹配项。
-
pattern:m
- 多行模式(详见 info:regexp-multiline-mode)。
-
pattern:s
-
启用 “dotall” 模式,允许点
pattern:.
匹配换行符\n
(在 info:regexp-character-classes 中有详细介绍)。 -
pattern:u
- 开启完整的 Unicode 支持。该修饰符能够正确处理代理对。详见 info:regexp-unicode。
-
pattern:y
- 粘滞模式,在文本中的确切位置搜索(详见 info:regexp-sticky)
接下来,各部分的颜色如下:
- 正则表达式 —— `pattern:red`
- 字符串(我们搜索的地方)—— `subject:blue`
- 结果 —— `match:green`
搜索:str.match
正如前面所提到的,将正则表达式和字符串方法结合一起使用。
str.match(regexp)
方法在字符串 str
中寻找 regexp
的所有匹配项。
它有 3 种工作模式:
如果正则表达式具有修饰符
pattern:g
,它返回一个由所有匹配项所构成的数组:let str = "We will, we will rock you";alert( str.match(/we/gi) ); // We,we(由两个匹配的子字符串构成的数组)
请注意,
match:We
和match:we
都被找到了,因为修饰符pattern:i
使得正则表达式在进行搜索时不区分大小写。如果没有这样的修饰符,它则会以数组形式返回第一个匹配项,索引
0
处保存着完整的匹配项,返回的结果的属性中还有一些其他详细信息:let str = "We will, we will rock you";let result = str.match(/we/i); // 没有修饰符 galert( result[0] ); // We(第一个匹配项) alert( result.length ); // 1// 详细信息: alert( result.index ); // 0(匹配项的位置) alert( result.input ); // We will, we will rock you(源字符串)
如果正则表达式中有一部分内容被包在括号里,那么返回的数组可能会有
0
以外的索引。我们将在 info:regexp-groups 中学习这部分相关内容。最后,如果没有匹配项,则返回
null
(无论是否有修饰符pattern:g
)。这是一个非常重要的细微差别。如果没有匹配项,我们不会收到一个空数组,而是会收到
null
。忘了这一点可能会导致错误,例如:let matches = "JavaScript".match(/HTML/); // = nullif (!matches.length) { // Error: Cannot read property 'length' of nullalert("Error in the line above"); }
如果我们希望结果始终是一个数组,我们可以这样写:
let matches = "JavaScript".match(/HTML/)*!* || []*/!*;if (!matches.length) {alert("No matches"); // 现在可以了 }
替换:str.replace
str.replace(regexp, replacement)
方法使用 replacement
替换在字符串 str
中找到的 regexp
的匹配项(如果带有修饰符 pattern:g
则替换所有匹配项,否则只替换第一个)。
例如:
// 没有修饰符 g
alert( "We will, we will".replace(/we/i, "I") ); // I will, we will// 带有修饰符 g
alert( "We will, we will".replace(/we/ig, "I") ); // I will, I will
第二个参数是字符串 replacement
。我们可以在其中使用特殊的字符组合来对匹配项进行插入:
符号 | 在替换字符串中的行为 |
---|---|
$&
|
插入整个匹配项 |
$`
|
插入字符串中匹配项之前的字符串部分 |
$'
|
插入字符串中匹配项之后的字符串部分 |
$n
|
如果 n 是一个 1-2 位的数字,则插入第 n 个分组的内容,详见 info:regexp-groups
|
$<name>
|
插入带有给定 name 的括号内的内容,详见 info:regexp-groups
|
$$
|
插入字符 $
|
带有 pattern:$&
的一个示例:
alert( "I love HTML".replace(/HTML/, "$& and JavaScript") ); // I love HTML and JavaScript
测试:regexp.test
regexp.test(str)
方法寻找至少一个匹配项,如果找到了,则返回 true
,否则返回 false
。
let str = "I love JavaScript";
let regexp = /LOVE/i;alert( regexp.test(str) ); // true
在后面的章节中,我们会学习更多正则表达式,通过更多的例子,也会遇到其他的方法。
关于这些方法的完整信息请见 info:regexp-methods。
总结
- 正则表达式由模式和可选择修饰符构成:
pattern:g
、pattern:i
、pattern:m
、pattern:u
、pattern:s
和pattern:y
。 - 没有修饰符和特殊符号(稍后我们会学到),那么正则表达式的搜索和子字符串的搜索相同。
str.match(regexp)
方法寻找匹配项:如果带有修饰符pattern:g
,则会返回所有匹配项,否则只会返回第一个匹配项。str.replace(regexp, replacement)
方法使用replacement
替换regexp
的匹配项:如果带有修饰符pattern:g
,则会替换所有匹配项,否则只会替换第一个匹配项。regexp.test(str)
方法用于测试,如果找到至少一个匹配项则返回true
,否则返回false
。
字符类
考虑一个实际的任务 —— 我们有一个电话号码,例如 "+7(903)-123-45-67"
,我们需要将其转换为纯数字:79031234567
。
为此,我们可以查找并删除所有非数字的内容。字符类可以帮助我们实现它。
字符类(Character classes) 是一种特殊的符号,匹配特定集合中的任何符号。
首先,让我们探索“数字”类。它写为 pattern:\d
,对应于“任何一位数字”。
例如,让我们找到电话号码的第一个数字:
let str = "+7(903)-123-45-67";let regexp = /\d/;alert( str.match(regexp) ); // 7
如果没有修饰符 pattern:g
,则正则表达式仅查找第一个匹配项,即第一个数字 pattern:\d
。
让我们添加修饰符 pattern:g
来查找所有数字:
let str = "+7(903)-123-45-67";let regexp = /\d/g;alert( str.match(regexp) ); // 匹配项构成的数组:7,9,0,3,1,2,3,4,5,6,7// 让我们将其输出为纯数字构成的电话号码:
alert( str.match(regexp).join('') ); // 79031234567
这是数字的字符类。还有其他字符类。
最常用的是:
-
pattern:\d
(“d” 来自 “digit”) -
数字:从
0
到9
的字符。 -
pattern:\s
(“s” 来自 “space”) -
空格符号:包括空格,制表符
\t
,换行符\n
和其他少数稀有字符,例如\v
、\f
和\r
。 -
pattern:\w
(“w” 来自 “word”) -
“单字”字符:拉丁字母或数字或下划线
_
。非拉丁字母(如西里尔字母或印地文)不属于pattern:\w
。
例如,pattern:\d\s\w
表示“数字”,后跟“空格字符”,后跟“单字字符”,例如 match:1 a
。
正则表达式可能同时包含常规符号和字符类。
例如,pattern:CSS\d
匹配 match:CSS
后面带有一个数字的字符串:
let str = "Is there CSS4?";
let regexp = /CSS\d/alert( str.match(regexp) ); // CSS4
我们还可以使用更多字符类:
alert( "I love HTML5!".match(/\s\w\w\w\w\d/) ); // ' HTML5'
匹配项(每个正则表达式字符类都有对应的结果字符):
反向类
对于每个字符类,都有一个“反向类”,用相同的字母表示,但是大写的。
“反向”表示它与所有其他字符匹配,例如:
-
pattern:\D
-
非数字:除
pattern:\d
以外的任何字符,例如字母。 -
pattern:\S
-
非空格符号:除
pattern:\s
以外的任何字符,例如字母。 -
pattern:\W
-
非单字字符:除
pattern:\w
以外的任何字符,例如非拉丁字母或空格。
在本章的开头,我们看到了如何从 subject:+7(903)-123-45-67
这样的字符串中创建一个只包含数字的电话号码:找到所有的数字并将它们连接起来。
let str = "+7(903)-123-45-67";alert( str.match(/\d/g).join('') ); // 79031234567
另一种快捷的替代方式是查找非数字 pattern:\D
并将其从字符串中删除:
let str = "+7(903)-123-45-67";alert( str.replace(/\D/g, "") ); // 79031234567
点(.)匹配“任何字符”
点 pattern:.
是一种特殊字符类,它与“除换行符之外的任何字符”匹配。
例如:
alert( "Z".match(/./) ); // Z
或在正则表达式中间:
let regexp = /CS.4/;alert( "CSS4".match(regexp) ); // CSS4
alert( "CS-4".match(regexp) ); // CS-4
alert( "CS 4".match(regexp) ); // CS 4(空格也是一个字符)
请注意,点表示“任何字符”,而不是“缺少字符”。必须有一个与之匹配的字符:
alert( "CS4".match(/CS.4/) ); // null,没有匹配项,因为这里没有与点匹配的字符
带有修饰符 “s” 时点字符类匹配任何字符
默认情况下,点与换行符 \n
不匹配。
例如,正则表达式 pattern:A.B
匹配 match:A
,然后匹配 match:B
和它们之间的任何字符,除了换行符\n
:
alert( "A\nB".match(/A.B/) ); // null(无匹配项)
在很多情况下,我们希望点能够匹配“任何字符”,包括换行符。
这就是修饰符 pattern:s
所做的事。如果有一个正则表达式具有该修饰符,那么点 pattern:.
能够匹配任何字符:
alert( "A\nB".match(/A.B/s) ); // A\nB(匹配了!)
warn header=“IE 浏览器不支持”
IE 浏览器不支持 pattern:s
修饰符。
幸运的是,有一个替代方案,我们可以使用像 pattern:[\s\S]
这样的正则表达式来匹配“任何字符”(此模式将在 info:regexp-character-sets-and-ranges 中介绍)。
alert( "A\nB".match(/A[\s\S]B/) ); // A\nB(匹配了!)
模式 pattern:[\s\S]
从字面上意思是:“空格字符或非空格字符”。换句话说,“任何字符”。我们可以使用另一对互补的类,例如 pattern:[\d\D]
。甚至是 pattern:[^]
—— 意思是匹配任何字符,除了什么都没有。
如果我们希望两种“点”都使用相同的模式,也可以使用此技巧:实际的点 pattern:.
表现常规方式(“不包括换行符”),以及带有 pattern:[\s\S]
或类似形式匹配“任何字符”。
warn header=“注意空格”
通常我们很少注意空格。对我们来说,字符串 subject:1-5
和 subject:1 - 5
几乎相同。
但是,如果正则表达式未考虑空格,则可能无法正常工作。
让我们尝试查找由连字符(-)分隔的数字:
alert( "1 - 5".match(/\d-\d/) ); // null,没有匹配项!
让我们修复一下,在正则表达式中添加空格 pattern:\d - \d
:
alert( "1 - 5".match(/\d - \d/) ); // 1 - 5,现在可以了
// 或者我们可以使用 \s 类:
alert( "1 - 5".match(/\d\s-\s\d/) ); // 1 - 5,也可以
一个空格是一个字符。与其他字符同等重要。
我们无法在正则表达式中添加或删除空格后,还期望它的作用能像之前那样不被改变。
换句话说,在正则表达式中,所有字符都很重要,空格也很重要。
总结
存在以下字符类:
pattern:\d
—— 数字。pattern:\D
—— 非数字。pattern:\s
—— 空格符号,制表符,换行符。pattern:\S
—— 除了pattern:\s
。pattern:\w
—— 拉丁字母,数字,下划线'_'
。pattern:\W
—— 除了pattern:\w
。pattern:.
—— 带有修饰符's'
时匹配任何字符,否则匹配除换行符\n
之外的任何字符。
……但这还不是全部!
JavaScript 用于字符串的 Unicode 编码提供了很多字符属性,例如:这个字母属于哪种语言(如果它是一个字母),它是标点符号吗,等等。
我们也可以通过这些属性进行搜索。这需要修饰符 pattern:u
。
Unicode:修饰符 “u” 和类 \p{…}
JavaScript 对字符串使用 Unicode 编码。大多数字符使用 2 个字节编码,但这种方式只能编码最多 65536 个字符。
这个范围不足以对所有可能的字符进行编码,这就是为什么使用 4 个字节对一些罕见的字符进行编码,比如 PostgreSQL源码学习(1)–PG13代码结构 PostgreSQL代码结构 Bootstrap:用于支持Bootstrap运行模式,该模式主要用来创建初始的模板数据库. Main:主程序模块, ... 准备工作: yara源码 V1.7.1 https://github.com/VirusTotal/yara/releases/tag/v1.7.1 csdn:https://download. ... 接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ... 一.最基本的使用 1.Maven依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId&g ... 前言 Vuex源码系列不知不觉已经到了第六篇.前置的五篇分别如下: 长篇连载:Vuex源码学习(一)功能梳理 长篇连载:Vuex源码学习(二)脉络梳理 作为一个Web前端,你知道Vuex的instal ... 前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ... Tomcat源码学习--WebAppClassLoader类加载机制 在WebappClassLoaderBase中重写了ClassLoader的loadClass方法,在这个实现方法中我们可以一窥t ... jQuery源码学习之Callbacks jQuery的ajax.deferred通过回调实现异步,其实现核心是Callbacks. 使用方法 使用首先要先新建一个实例对象.创建时可以传入参数flag ... 一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ... --本文是<项目驱动学习--DotText源码学习>系列的第一篇文章,在这之后会持续发表相关的文章. 概论 在阅读DotText源码之前,让我们首先了解一下ASP.NET的工作机制,可以使 ...
【源码学习】正则表达式相关推荐
最新文章
热门文章