JavaScript 正则表达式核心概念解析 – wiki词典


JavaScript 正则表达式核心概念解析

正则表达式(Regular Expression,简称 Regex 或 Regexp)是用于描述字符模式的强大工具,在 JavaScript 中广泛应用于字符串的搜索、替换、验证和提取等操作。掌握正则表达式,能够极大地提升处理文本数据的效率和灵活性。本文将深入解析 JavaScript 正则表达式的核心概念。

1. 正则表达式的创建

在 JavaScript 中,创建正则表达式主要有两种方式:

1.1 正则表达式字面量 (Literal Notation)

这是最常用、推荐的方式,尤其当正则表达式模式保持不变时。它在脚本加载时编译,性能较优。

“`javascript
const regexLiteral = /pattern/flags;

// 示例:匹配 “hello”
const re1 = /hello/;
// 匹配不区分大小写的 “apple”
const re2 = /apple/i;
“`

1.2 RegExp 构造函数 (Constructor Function)

当正则表达式的模式是动态生成或来自变量时,可以使用 RegExp 构造函数。它在运行时编译。

“`javascript
const regexConstructor = new RegExp(‘pattern’, ‘flags’);

// 示例:匹配变量中的单词
const wordToMatch = “world”;
const re3 = new RegExp(wordToMatch);
// 匹配动态模式且全局不区分大小写
const dynamicPattern = “abc”;
const re4 = new RegExp(dynamicPattern, “gi”);
``
**注意:** 如果在构造函数中使用字符串作为模式,需要对特殊字符进行双重转义(例如,匹配点
.需要new RegExp(‘\.’)`)。

2. RegExp 对象的核心方法

RegExp 对象自身提供了两个主要方法用于模式匹配:

2.1 test()

test() 方法用于检测一个字符串是否包含与正则表达式匹配的模式。如果找到匹配项,返回 true;否则,返回 false

javascript
const regex = /hello/;
console.log(regex.test("hello world")); // true
console.log(regex.test("hi there")); // false

2.2 exec()

exec() 方法执行搜索匹配。如果找到匹配项,它返回一个数组,包含匹配的文本、捕获组内容以及匹配的索引等信息;如果没有找到匹配项,则返回 null

如果正则表达式带有 g (global) 标志,exec() 会迭代地返回每个匹配项,并且每次调用都会更新正则表达式实例的 lastIndex 属性,指向下一次搜索的起始位置。

“`javascript
const regex = /world/;
const result = regex.exec(“hello world”);
console.log(result);
/
输出示例:
[
‘world’,
index: 6,
input: ‘hello world’,
groups: undefined
]
/

// 带有 ‘g’ 标志的 exec() 示例
const regexGlobal = /o/g;
let match;
while ((match = regexGlobal.exec(“hello world”)) !== null) {
console.log(找到 '${match[0]}' 在索引 ${match.index});
}
/
输出:
找到 ‘o’ 在索引 4
找到 ‘o’ 在索引 7
/
“`

3. 字符串对象与正则表达式相关的方法

JavaScript 的 String 对象也提供了一系列方法,可以直接接受正则表达式作为参数,进行更高级的字符串操作。

3.1 match()

match() 方法检索字符串中与正则表达式匹配的结果。
* 如果正则表达式没有 g 标志,它返回第一个匹配项的数组(与 exec() 返回的结构类似)。
* 如果带有 g 标志,它返回所有匹配项组成的数组,但不包含捕获组信息。如果没有匹配,返回 null

“`javascript
const str = “The quick brown fox jumps over the lazy dog.”;

console.log(str.match(/fox/));
// [‘fox’, index: 16, input: ‘The quick brown fox jumps over the lazy dog.’, groups: undefined]

console.log(str.match(/o/g)); // [‘o’, ‘o’, ‘o’, ‘o’]
console.log(str.match(/xyz/)); // null
“`

3.2 matchAll()

matchAll() 方法返回一个迭代器,其中包含所有匹配项及其捕获组。它总是需要正则表达式带有 g 标志。

“`javascript
const str = “cat, bat, hat”;
const regex = /(\w)at/g;

for (const match of str.matchAll(regex)) {
console.log(match);
}
/
输出:
[‘cat’, ‘c’, index: 0, input: ‘cat, bat, hat’, groups: undefined]
[‘bat’, ‘b’, index: 5, input: ‘cat, bat, hat’, groups: undefined]
[‘hat’, ‘h’, index: 10, input: ‘cat, bat, hat’, groups: undefined]
/
“`

3.3 replace() / replaceAll()

  • replace(): 用替换字符串或函数替换匹配的模式。如果正则表达式没有 g 标志,只替换第一个匹配项;如果带有 g 标志,替换所有匹配项。
  • replaceAll(): 替换字符串中所有匹配的模式。它总是需要正则表达式带有 g 标志,或者接受一个字符串作为第一个参数。

javascript
const str = "apple, banana, apple";
console.log(str.replace(/apple/, "orange")); // "orange, banana, apple"
console.log(str.replace(/apple/g, "orange")); // "orange, banana, orange"
console.log(str.replaceAll("apple", "orange")); // "orange, banana, orange"
console.log(str.replaceAll(/apple/g, "orange")); // "orange, banana, orange"

3.4 search()

search() 方法搜索字符串中与正则表达式匹配的第一个子字符串,并返回其起始索引。如果没有找到,返回 -1

javascript
const str = "hello world";
console.log(str.search(/world/)); // 6
console.log(str.search(/foo/)); // -1

3.5 split()

split() 方法使用正则表达式或固定字符串作为分隔符,将字符串分割成一个字符串数组。

javascript
const str = "one,two;three-four";
console.log(str.split(/[,;-]/)); // ['one', 'two', 'three', 'four']

4. 正则表达式模式详解

正则表达式模式由各种字符和特殊符号组成,它们定义了要搜索的模式。

4.1 字面量字符 (Literal Characters)

大多数字符都直接匹配它们自身。例如 /abc/ 会精确匹配字符串 “abc”。

4.2 特殊字符 / 元字符 (Special Characters / Metacharacters)

这些字符在正则表达式中有特殊含义,需要转义 (用 \ 前缀) 才能匹配其字面值。

  • .:匹配除换行符 (\n, \r) 之外的任何单个字符。(如果设置了 s 标志,则包括换行符)。
  • \d:匹配任何数字字符 (等同于 [0-9])。
  • \D:匹配任何非数字字符 (等同于 [^0-9])。
  • \w:匹配任何单词字符 (字母、数字、下划线,等同于 [a-zA-Z0-9_])。
  • \W:匹配任何非单词字符 (等同于 [^a-zA-Z0-9_])。
  • \s:匹配任何空白字符 (空格、制表符 \t、换行符 \n、回车符 \r、换页符 \f 等)。
  • \S:匹配任何非空白字符。
  • \b:匹配单词边界 (例如,\bword\b 会匹配独立的 “word”)。
  • \B:匹配非单词边界。

4.3 字符集 (Character Sets)

使用方括号 [] 定义,匹配方括号中的任何一个字符

  • [abc]:匹配 ‘a’、’b’ 或 ‘c’。
  • [a-z]:匹配任何小写字母。
  • [A-Z]:匹配任何大写字母。
  • [0-9]:匹配任何数字。
  • [a-zA-Z0-9]:匹配任何字母或数字。
  • [^abc]:匹配除 ‘a’、’b’、’c’ 之外的任何字符 (负向字符集)。

4.4 量词 (Quantifiers)

量词用于指定前一个模式元素出现的次数。

  • *:匹配前一个元素零次或多次 (0 或更多次)。
  • +:匹配前一个元素一次或多次 (1 或更多次)。
  • ?:匹配前一个元素零次或一次 (可选)。
  • {n}:匹配前一个元素恰好 n 次。
  • {n,}:匹配前一个元素至少 n 次。
  • {n,m}:匹配前一个元素至少 n 次,但不超过 m 次。
  • ? (在量词后):使量词变为非贪婪模式 (lazy),尽可能少地匹配。默认情况下,量词是贪婪的,会尽可能多地匹配。

javascript
/a+/ // 匹配一个或多个 'a' (例如 'a', 'aaa')
/ab?c/ // 匹配 'ac' 或 'abc'
/\d{3}/ // 匹配恰好三个数字 (例如 '123')
/a.*?b/ // 非贪婪匹配,匹配 'a' 到最近的 'b'

4.5 锚点 (Anchors)

锚点用于匹配字符串中的特定位置,而不是特定的字符。

  • ^:匹配字符串的开头。(如果设置了 m 标志,则匹配每行的开头)。
  • $:匹配字符串的结尾。(如果设置了 m 标志,则匹配每行的结尾)。

javascript
/^hello/ // 匹配以 "hello" 开头的字符串
/world$/ // 匹配以 "world" 结尾的字符串
/^exact$/ // 匹配只包含 "exact" 的字符串

4.6 或 (Alternation)

使用 | 运算符可以匹配多个模式中的任意一个。

javascript
/cat|dog/ // 匹配 "cat" 或 "dog"
/(red|blue) car/ // 匹配 "red car" 或 "blue car"

5. 标志 (Flags)

标志是可选参数,它们修改正则表达式的搜索行为。

  • g (global):全局匹配。查找所有匹配项,而不是在找到第一个匹配项后停止。
  • i (case-insensitive):不区分大小写匹配。
  • m (multiline):多行模式。使 ^$ 匹配每行的开头和结尾,而不仅仅是整个字符串的开头和结尾。
  • u (unicode):启用完整的 Unicode 支持,正确处理超出基本多语言平面 (BMP) 的 Unicode 字符(如表情符号)。
  • s (dotAll):点号 . 匹配所有字符,包括换行符。
  • y (sticky):粘性匹配。只在正则表达式的 lastIndex 属性指示的精确位置进行匹配。

javascript
"Hello World".match(/hello/i); // ['Hello']
"abc abc".match(/abc/g); // ['abc', 'abc']
"Line1\nLine2".match(/^Line/gm); // ['Line', 'Line']
"😊".match(/./u); // ['😊']

6. 组与反向引用 (Groups and Backreferences)

6.1 捕获组 () (Capturing Groups)

捕获组将多个模式组合成一个单元,并“捕获”匹配的子字符串,以便后续引用。捕获组从 1 开始编号。

javascript
const regex = /(\d{4})-(\d{2})-(\d{2})/;
const match = regex.exec("今天是 2023-10-26");
console.log(match[0]); // "2023-10-26" (整个匹配)
console.log(match[1]); // "2023" (第一个捕获组)
console.log(match[2]); // "10" (第二个捕获组)
console.log(match[3]); // "26" (第三个捕获组)

6.2 非捕获组 (?:) (Non-capturing Groups)

非捕获组也将多个模式组合成一个单元,但不会捕获匹配的子字符串。这有助于提高性能,尤其是在你不需要引用组内容时。

javascript
const regex = /(?:abc)+/; // 匹配一个或多个 "abc",但不捕获 "abc"

6.3 命名捕获组 (?<name>...) (Named Capturing Groups)

允许为捕获组指定名称,而不是通过数字索引访问。这使得代码更具可读性。

javascript
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = regex.exec("日期: 2023-10-26");
console.log(match.groups.year); // "2023"
console.log(match.groups.month); // "10"
console.log(match.groups.day); // "26"

6.4 反向引用 \n\k<name> (Backreferences)

在正则表达式模式中引用前面捕获组匹配到的内容。
* \n 引用第 n 个捕获组。
* \k<name> 引用命名捕获组。

“`javascript
// 匹配重复的单词
const regex = /(\w+)\s\1/;
console.log(regex.test(“hello hello”)); // true
console.log(regex.test(“world wide”)); // false

// 命名反向引用
const regexNamed = /(?\w+)\s\k/;
console.log(regexNamed.test(“test test”)); // true
“`

6.5 断言 (Assertions)

断言用于在不实际消耗字符的情况下检查匹配位置周围的条件。

  • 先行断言 (?=...) (Lookahead Assertion):匹配后面跟着特定模式的文本,但不包含该模式本身。
    javascript
    /foo(?=bar)/ // 匹配 "foobar" 中的 "foo",但只在后面跟着 "bar" 时
    "foobar".match(/foo(?=bar)/); // ['foo']
    "foobaz".match(/foo(?=bar)/); // null

  • 先行否定断言 (?!...) (Negative Lookahead Assertion):匹配后面没有跟着特定模式的文本。
    javascript
    /foo(?!bar)/ // 匹配 "foo" 但不匹配 "foobar" 中的 "foo"
    "foobaz".match(/foo(?!bar)/); // ['foo']
    "foobar".match(/foo(?!bar)/); // null

  • 后行断言 (?<=...) (Lookbehind Assertion):匹配前面是特定模式的文本,但不包含该模式本身。
    javascript
    /(?<=foo)bar/ // 匹配 "foobar" 中的 "bar",但只在前面是 "foo" 时
    "foobar".match(/(?<=foo)bar/); // ['bar']
    "bazbar".match(/(?<=foo)bar/); // null

  • 后行否定断言 (?<!...) (Negative Lookbehind Assertion):匹配前面不是特定模式的文本。
    javascript
    /(?<!foo)bar/ // 匹配 "bar" 但不匹配 "foobar" 中的 "bar"
    "bazbar".match(/(?<!foo)bar/); // ['bar']
    "foobar".match(/(?<!foo)bar/); // null

总结

JavaScript 正则表达式是处理文本的瑞士军刀。通过熟练运用字面量、元字符、字符集、量词、锚点、标志以及捕获组等核心概念,开发者可以高效地完成复杂的字符串匹配和操作任务。从简单的表单验证到复杂的文本解析,正则表达式都是前端和后端 JavaScript 开发中不可或缺的强大工具。持续练习和理解这些模式的组合使用,将使您在日常开发中更加得心应手。


滚动至顶部