学会JavaScript正则表达式(一)

文章目录

  • 深入理解JavaScript正则表达式
    • 1. 正则表达式基础
      • 1.1 创建正则表达式
        • 1.1.1 字面量语法
        • 1.1.2 构造函数
      • 1.2 匹配模式
        • 1.2.1 全局模式(g)
        • 1.2.2 不区分大小写模式(i)
        • 1.2.3 多行模式(m)
        • 总结
      • 1.3 特殊字符和字符类
        • 1.3.1 特殊字符和字符类
        • 代码示例
        • 总结

深入理解JavaScript正则表达式

正则表达式(Regular Expression,常简称为regex或regexp)是处理字符串的强大工具,它提供了一种灵活的方式来搜索、匹配(以及在某些情况下替换)文本中的子字符串。在JavaScript中,正则表达式是通过RegExp对象来表示的。本文将带你深入了解JavaScript中的正则表达式,包括其基础语法、常见模式、以及实际用例。

1. 正则表达式基础

1.1 创建正则表达式

在JavaScript中,你可以通过两种方式创建正则表达式:

  • 字面量语法:使用斜杠(/)包围的模式,如/ab+c/
  • 构造函数:使用new RegExp('ab+c')
1.1.1 字面量语法

字面量语法是创建正则表达式最直接的方式。你只需要将模式放在斜杠(/)之间即可。例如,如果你想匹配字符串中所有出现的“ab+c”模式(即一个“a”后面跟着一个或多个“b”,再后面是一个“c”),你可以这样写:

const regex = /ab+c/;

这个正则表达式可以用来测试字符串中是否存在符合该模式的子串:

const str = 'abc abbc abbbbbc';
const result = str.match(regex);
console.log(result); // 输出: [ 'abc', index: 0, input: 'abc abbc abbbbbc', groups: undefined ]

注意,在这个例子中,match 方法返回了一个数组,其中包含了匹配到的所有结果。由于我们使用了贪婪量词(+),它会尽可能多地匹配“b”。然而,正则表达式是按照左到右的顺序进行匹配的,所以第一个匹配到的是“abc”。

1.1.2 构造函数

另一种创建正则表达式的方式是使用RegExp构造函数。这种方式在模式需要动态生成时特别有用。构造函数接受两个参数:模式字符串和可选的标志字符串。例如:

const pattern = 'ab+c';
const flags = 'i'; // 不区分大小写
const regex = new RegExp(pattern, flags);

这个正则表达式与之前的字面量语法创建的等价,但是它不区分大小写:

const str = 'aBc AbBc aBBBBc';
const result = str.match(regex);
console.log(result); // 输出: [ 'aBc', index: 0, input: 'aBc AbBc aBBBBc', groups: undefined ]

在这个例子中,由于我们设置了“i”标志,所以正则表达式在匹配时忽略了大小写。因此,它能够匹配到“aBc”。

请注意,当使用构造函数创建正则表达式时,由于字符串中的反斜杠()是转义字符,你可能需要对它进行双重转义。例如,如果你想在构造函数中表示d(匹配任何数字),你需要这样写:

const regex = new RegExp('\d');

或者,你可以使用原始字符串字面量(在ES6及更高版本中可用),这样就不需要对反斜杠进行转义:

const regex = new RegExp(`\d`); // 实际上不需要双重转义,但这里为了演示
// 或者更简洁地
const regex = /d/; // 直接使用字面量语法,通常更可取

在实际开发中,如果正则表达式的模式是固定的,推荐使用字面量语法,因为它的语法更简洁,性能也更好。如果模式需要根据运行时的情况动态生成,那么构造函数是一个更好的选择。

1.2 匹配模式

1.2.1 全局模式(g)

全局模式意味着正则表达式会搜索整个字符串以找到所有匹配项,而不仅仅是找到第一个匹配项就停止。如果没有使用全局模式,match 方法在找到第一个匹配项后就会返回,不会再继续搜索字符串的剩余部分。

代码示例:

const regexNonGlobal = /foo/; // 非全局模式
const regexGlobal = /foo/g; // 全局模式

const str = 'foo and foo';

const matchesNonGlobal = str.match(regexNonGlobal);
const matchesGlobal = str.match(regexGlobal);

console.log(matchesNonGlobal); // 输出: [ 'foo', index: 0, input: 'foo and foo', groups: undefined ]
console.log(matchesGlobal); // 输出: [ 'foo', 'foo' ]

在这个例子中,非全局模式的正则表达式只匹配到了第一个“foo”,而全局模式的正则表达式匹配到了所有出现的“foo”。

1.2.2 不区分大小写模式(i)

不区分大小写模式使正则表达式在匹配时忽略字符的大小写。这意味着它会将大写和小写字符视为相同。

代码示例:

const regexCaseSensitive = /FOO/; // 区分大小写
const regexCaseInsensitive = /FOO/i; // 不区分大小写

const str = 'FOO and foo';

const matchesCaseSensitive = str.match(regexCaseSensitive);
const matchesCaseInsensitive = str.match(regexCaseInsensitive);

console.log(matchesCaseSensitive); // 输出: [ 'FOO', index: 0, input: 'FOO and foo', groups: undefined ]
console.log(matchesCaseInsensitive); // 输出: [ 'FOO', 'foo' ]

在这个例子中,区分大小写的正则表达式只匹配到了大写的“FOO”,而不区分大小写的正则表达式匹配到了所有出现的“FOO”和“foo”。

1.2.3 多行模式(m)

多行模式改变了^$的行为。在默认的单行模式下,^匹配字符串的开始,$匹配字符串的结束。但在多行模式下,^$也会匹配任何行的开始和结束。

代码示例:

const regexSingleLine = /^foo/; // 单行模式
const regexMultiLine = /^foo/m; // 多行模式

const str = 'foo
foo';

const matchesSingleLine = str.match(regexSingleLine);
const matchesMultiLine = str.match(regexMultiLine);

console.log(matchesSingleLine); // 输出: [ 'foo', index: 0, input: 'foo
foo', groups: undefined ]
console.log(matchesMultiLine); // 在某些环境中可能不会按预期工作,因为match()方法不完全支持多行模式

// 使用exec()方法在多行字符串中查找所有匹配项
let regexMultiLineExec = /^foo/m;
let result;
while ((result = regexMultiLineExec.exec(str)) !== null) {
  console.log(result[0]); // 输出每行匹配的'foo'
}
// 输出:
// foo
// foo

注意:在使用match方法时,多行模式可能不会按预期工作,因为match方法返回的是整个字符串中的匹配项,而不是每行一个。为了在多行字符串中查找每行的匹配项,通常使用exec方法在循环中多次调用。

总结
模式 描述 示例
全局(g) 搜索整个字符串以找到所有匹配项 /foo/g
不区分大小写(i) 在匹配时忽略大小写 /FOO/i
多行(m) 使^$匹配任何行的开始和结束 /^foo/m (使用exec方法循环查找)

在实际应用中,这些模式可以单独使用,也可以组合使用,以满足特定的匹配需求。例如,/foo/gi会匹配字符串中所有出现的“foo”,不区分大小写。

1.3 特殊字符和字符类

1.3.1 特殊字符和字符类
  • d:匹配任何数字字符,等同于[0-9]
  • w:匹配任何字母、数字或下划线字符,等同于[A-Za-z0-9_]
  • .:匹配除换行符(

    )之外的任何字符。
  • *:匹配前面的子表达式零次或多次。
  • +:匹配前面的子表达式一次或多次。
  • ?:匹配前面的子表达式零次或一次。
  • {n}:n 是一个非负整数,匹配前面的子表达式恰好 n 次。
  • {n,}:n 是一个非负整数,匹配前面的子表达式至少 n 次。
  • {n,m}:m 和 n 均为非负整数,且 n <= m,匹配前面的子表达式至少 n 次且最多 m 次。
代码示例
// d 示例:匹配数字
const regexDigits = /d+/;
const strDigits = '123abc456';
const matchDigits = strDigits.match(regexDigits);
console.log(matchDigits[0]); // 输出: "123" (只会匹配到第一个连续的数字序列)

// w 示例:匹配字母、数字或下划线
const regexWordChars = /w+/;
const strWordChars = 'hello_world123';
const matchWordChars = strWordChars.match(regexWordChars);
console.log(matchWordChars[0]); // 输出: "hello_world123"

// . 示例:匹配任意字符(换行符除外)
const regexAnyChar = /a.b/;
const strAnyChar = 'axb
ayb';
const matchAnyChar = strAnyChar.match(regexAnyChar);
console.log(matchAnyChar[0]); // 输出: "axb" (不会匹配换行后的"ayb")

// * 示例:匹配前面的字符零次或多次
const regexStar = /ab*c/;
const strStar = 'acabcabbc';
const matchStar = strStar.match(regexStar);
console.log(matchStar[0]); // 输出: "abbc" (匹配尽可能多的"b")

// + 示例:匹配前面的字符一次或多次
const regexPlus = /ab+c/;
const strPlus = 'acabcabbc';
const matchPlus = strPlus.match(regexPlus);
console.log(matchPlus[0]); // 输出: "abbc" (至少匹配一次"b")

// ? 示例:匹配前面的字符零次或一次
const regexQuestion = /ab?c/;
const strQuestion = 'acbabc';
const matchQuestion = strQuestion.match(regexQuestion);
console.log(matchQuestion[0]); // 输出: "ac" (可以没有"b")

// {n} 示例:匹配前面的字符恰好 n 次
const regexExact = /ab{2}c/;
const strExact = 'abbcabcab';
const matchExact = strExact.match(regexExact);
console.log(matchExact[0]); // 输出: "abbc" (恰好匹配两次"b")

// {n,} 示例:匹配前面的字符至少 n 次
const regexAtLeast = /ab{2,}c/;
const strAtLeast = 'abbcabbbbc';
const matchAtLeast = strAtLeast.match(regexAtLeast);
console.log(matchAtLeast[0]); // 输出: "abbbbc" (至少匹配两次"b",尽可能多地匹配)

// {n,m} 示例:匹配前面的字符至少 n 次,但不超过 m 次
const regexRange = /ab{2,4}c/;
const strRange = 'abbcabbbbcabbbbbc';
const matchRange = strRange.match(regexRange);
console.log(matchRange[0]); // 输出: "abbbbc" (匹配两到四次"b",尽可能多地匹配)
总结
特殊字符/字符类 描述 示例
d 匹配任何数字字符 d 匹配 “1”, “2”, “3” 等
w 匹配任何字母、数字或下划线字符 w 匹配 “a”, “B”, “5”, “_” 等
. 匹配除换行符之外的任何字符 . 匹配 “a”, “b”, “c” 但不匹配 “
* 匹配前面的子表达式零次或多次 ab*c 匹配 “ac”, “abc”, “abbc” 等
+ 匹配前面的子表达式一次或多次 ab+c 匹配 “abc”, “abbc” 但不匹配 “ac”
? 匹配前面的子表达式零次或一次 ab?c 匹配 “ac”, “abc” 但不匹配 “abbc”
{n} 匹配前面的子表达式恰好 n 次 ab{2}c 匹配 “abbc” 但不匹配 “abc” 或 “abbbc”
{n,} 匹配前面的子表达式至少 n 次 ab{2,}c 匹配 “abbc”, “abbbbc” 等,但不匹配 “abc”
{n,m} 匹配前面的子表达式至少 n 次且最多 m 次 ab{2,4}c 匹配 “abbc”, “abbbbc” 但不匹配 “abc” 或 “abbbbbbc”

这个图表总结了特殊字符和字符类的用法,以及它们如何与数量词结合使用来定义更具体的匹配模式。在实际应用中,这些特殊字符和字符类可以单独使用,也可以组合使用,以满足特定的匹配需求。