JavaScript 正则表达式(RegExp)实用指南【译】

Posted by lamyoung on October 6, 2019

快速实践指南。用示例理解正则表达式。

当初次见到正则表达式时,它们可能看起来像是乱七八糟的随机字符串。尽管它们看起来语法有些混乱,但它们其实是非常有用。

正确理解正则表达式能帮助你成为更有效的程序员。为了全面了解正则表达式世界,首先需要学习基础概念,然后在其基础上扩展。

事不宜迟,让我们开始吧:)

什么是正则表达式?

正则表达式是用于匹配字符串中字符组合的模式。它们构成了自己的一门小语言,是许多编程语言(如Javascript,Perl,Python,Php和Java)的一部分。

正则表达式帮助你可以检查例如电子邮件地址或密码之类的字符串是否符合一定的模式,进一步处理相关信息和提示。

创建一个正则表达式

有两种方法可以在Javascript中创建正则表达式。 可以使用RegExp构造函数创建它,也可以使用两个正斜杠(/)。

正则表达式构造函数:

语法new RegExp(pattern [,flags])

例如:

var regexConst = new RegExp('abc');

正则表达式字面量:

语法/pattern/flags

例如:

var regexLiteral = /abc/;
  • 这些标志(flags)是可选的,我将在本文后面解释。

在某些情况下,可能想动态创建正则表达式,在这种情况下,正则表达式 字面量不起作用,因此必须使用正则表达式构造函数。

无论选择哪种方法,结果都将是一个正则表达式对象。 两个正则表达式对象都将具有相同的方法和属性。

由于在上面的示例中使用两个正斜杠,因此,如果要将正斜杠(/)用作正则表达式的一部分,则必须使用反斜杠(\)进行转义。

正则表达式方法

测试正则表达式的方法主要有两种。

RegExp.prototype.test()

一个在字符串中测试是否匹配的RegExp方法,它返回 truefalse

例如:

var regex = /hello/;
var str = 'hello world';
var result = regex.test(str);
console.log(result);
// returns true

RegExp.prototype.exec()

一个在字符串中执行查找匹配的RegExp方法,它返回一个数组(未匹配到则返回 null)。

例如:

var regex = /hello/;
var str = 'hello world';
var result = regex.exec(str);
console.log(result);
// returns [ 'hello', index: 0, input: 'hello world', groups: undefined ]
// 'hello' -> 匹配的全部字符串
// index: -> 匹配到的字符位于原始字符串的基于0的索引值
// input: -> 原始字符串

我们将在本文使用 test() 方法。

简单的正则表达式模式

这是最基本的模式,只需将文字文本与测试字符串匹配即可。 例如:

var regex = /hello/;
console.log(regex.test('hello world'));
// true

特殊字符

到目前为止,我们已经创建了简单的正则表达式模式。 现在,让我们在处理更复杂的情况时充分利用正则表达式的全部功能。

例如,我们在一堆邮箱地址中,我们要排除qq邮箱。 那是特殊字符起作用的地方。 为了完全理解正则表达式,你必须记住一些特殊的符号和字符。

标志

正则表达式具有五个可选的标志或修饰符。 让我们讨论两个最重要的标志:

  • g — 常用于执行一个全局搜索匹配, 即(不仅仅返回第一个匹配的, 而是返回全部)
  • i — 不区分大小写搜索。

还可以在单个正则表达式中用多个标志。他们的顺序对结果没有任何影响。

让我们看一些代码示例:

正则表达式字面量 — 语法 /pattern/flags
var regexGlobal = /abc/g;
console.log(regexGlobal.test('abc abc'));
// 它将匹配所有的'abc',在第一次匹配后不会返回
var regexInsensitive = /abc/i;
console.log(regexInsensitive.test('Abc'));
// returns true, 因为忽略大小写
正则表达式构造函数 — 语法 new RegExp('pattern', 'flags')
var regexGlobal = new RegExp('abc','g')
console.log(regexGlobal.test('abc abc'));
// 它将匹配所有的'abc',在第一次匹配后不会返回
var regexInsensitive = new RegExp('abc','i')
console.log(regexInsensitive.test('Abc'));
// returns true, 因为忽略大小写

字符集:

[xyz] — 字符集是在一个位置匹配不同字符的一种方法,它可以匹配括号内出现的字符中字符串中的任何一个字符。 例如:

var regex = /[bt]ear/;
console.log(regex.test('tear'));
// returns true
console.log(regex.test('bear'));
// return true
console.log(regex.test('fear'));
// return false

[^xyz]^用在一个方括号的开头的时候, 它表示这个字符集是否定的,它可以匹配除了括号内出现的字符中字符串中的任何一个字符。 例如:

var regex = /[^bt]ear/;
console.log(regex.test('tear'));
// returns false
console.log(regex.test('bear'));
// return false
console.log(regex.test('fear'));
// return true

[a-z] — 如果我们想将一个字母的所有字母都匹配在一个位置,我们可以将所有字母写在方括号内,但是有一种更简单的方法,那就是范围。 例如:[a-h]将匹配从a到h的所有字母,[0-9]将匹配从a到h的所有数字,[A-Z]将匹配所有大写字母。

var regex = /[a-z]ear/;
console.log(regex.test('fear'));
// returns true
console.log(regex.test('tear'));
// returns true

元字符 — 元字符是具有特殊含义的字符。 一共有很多元字符,但我将在这里介绍最重要的字符。

  • \d — 匹配一个数字。等价于[0-9]
  • \w — 匹配一个单字字符(字母、数字或者下划线)。等价于 [A-Za-z0-9_]。即字母数字字符。
  • \s — 匹配一个空白字符,包括空格、制表符、换页符和换行符。
  • \t — 匹配一个水平制表符 (U+0009)。
  • \b — 匹配单词的开头或结尾。 也称为字边界。
  • . — (小数点)默认匹配除换行符之外的任何单个字符。
  • \D — 匹配一个非数字字符。等价于[^0-9]
  • \W — 匹配一个非单字字符。等价于 [^A-Za-z0-9_]
  • \S — 匹配一个非空白字符。

量词 — 量词是在正则表达式中具有特殊含义的符号。

  • + — 匹配前面一个表达式 1 次或者多次。
    var regex = /\d+/;
    console.log(regex.test('8'));
    // true
    console.log(regex.test('88899'));
    // true
    console.log(regex.test('8888845'));
    // true
    
  • * — 匹配前一个表达式 0 次或多次。
    var regex = /go*d/;
    console.log(regex.test('gd'));
    // true
    console.log(regex.test('god'));
    // true
    console.log(regex.test('good'));
    // true
    console.log(regex.test('goood'));
    // true
    
  • ? — 匹配前面一个表达式 0 次或者 1 次。
    var regex = /goo?d/;
    console.log(regex.test('god'));
    // true
    console.log(regex.test('good'));
    // true
    console.log(regex.test('goood'));
    // false
    
  • ^ — 匹配字符串的开头,紧随其后的正则表达式应位于测试字符串的开头。
    var regex = /^g/;
    console.log(regex.test('good'));
    // true
    console.log(regex.test('bad'));
    // false
    console.log(regex.test('tag'));
    // false
    
  • $ — 匹配字符串的末尾,即在它之前的正则表达式应位于测试字符串的末尾。
    var regex = /.com$/;
    console.log(regex.test('test@testmail.com'));
    // true
    console.log(regex.test('test@testmail'));
    // false
    
  • {N} — N 是一个正整数,匹配了前面一个字符刚好出现了 N 次。
    var regex = /go{2}d/;
    console.log(regex.test('good'));
    // true
    console.log(regex.test('god'));
    // false
    
  • {N,} — N 是一个正整数,匹配前一个字符至少出现了 N 次。
    var regex = /go{2,}d/;
    console.log(regex.test('good'));
    // true
    console.log(regex.test('goood'));
    // true
    console.log(regex.test('gooood'));
    // true
    
  • {N,M} — N 和 M 都是整数。匹配前面的字符至少 N 次,最多 M 次。
    var regex = /go{1,2}d/;
    console.log(regex.test('god'));
    // true
    console.log(regex.test('good'));
    // true
    console.log(regex.test('goood'));
    // false
    
  • X|Y — 匹配 X 或者 Y
    var regex = /(green|red) apple/;
    console.log(regex.test('green apple'));
    // true
    console.log(regex.test('red apple'));
    // true
    console.log(regex.test('blue apple'));
    // false
    

如果表达式中包括特殊字符, 例如要匹配 +., 就必须使用转义符号(\).

var regex = /a+b/;  // This won't work
var regex = /a\+b/; // This will work
console.log(regex.test('a+b')); // true

高级一点的用法

(x) — 匹配x并记住匹配。 这些称为捕获组。 这也用于在正则表达式中创建子表达式。 例如 :

var regex = /(foo)bar\1/;
console.log(regex.test('foobarfoo'));
// true
console.log(regex.test('foobar'));
// false

\1 记住并使用括号中第一个子表达式中的匹配项(上面的例子 \1 等同于 foo )。

(?:x) — 匹配x,不记住匹配。 这些称为非捕获组。 这里的\1将不起作用,它将与字符串 \1匹配。

var regex = /(?:foo)bar\1/;
console.log(regex.test('foobarfoo'));
// false
console.log(regex.test('foobar'));
// false
console.log(regex.test('foobar\1'));
// true

x(?=y) — 匹配 x 仅仅当 x 后面跟着 y 。这种叫做先行断言。

var regex = /Red(?=Apple)/;
console.log(regex.test('RedApple'));
// true

在上面的示例中,仅当Red紧跟Apple时,匹配才会发生。

练习正则表达式

让我们练习上面学到的一些概念。

  • 匹配任何10位数字:
    var regex = /^\d{10}$/;
    console.log(regex.test('9995484545'));
    // true
    

    让我们分解一下,看看每步做了什么。

    1. 如果我们要强制匹配必须跨越整个字符串,则可以添加量词^$。 插入符号^与输入字符串的开头匹配,而美元符号$与结尾匹配。 因此,如果字符串包含的位数超过10位,则不会匹配。
    2. \d匹配任何数字字符。
    3. {10}与上一个表达式匹配,在这种情况下,\d恰好是10倍。 因此,如果测试字符串包含的位数少于或大于10,则结果将为false
  • 匹配 以DD-MM-YYYYDD-MM-YY格式 的日期。
    var regex = /^(\d{1,2}-){2}\d{2}(\d{2})?$/;
    console.log(regex.test('01-01-1990'));
    // true
    console.log(regex.test('01-01-90'));
    // true
    console.log(regex.test('01-01-190'));
    // false
    

    让我们分解一下,看看每步做了什么。

    1. 同样地,我们将整个正则表达式包装在^$内,以使匹配跨整个字符串。
    2. ( 第一个子表达式的开始。
    3. \d{1,2} 匹配至少1位和最多2位数字。
    4. - 匹配文字连字符-
    5. ) 第一个子表达式的结尾。
    6. {2} 与第一个子表达式完全匹配两次。
    7. \d{2} 精确匹配两位数。
    8. (\d{2})? 精确匹配两位数。 但是它是可选的,因此年份包含2位数字或4位数字。
  • 匹配除换行符之外的所有内容
    表达式应匹配任何格式如abc.def.ghi.jkl的字符串,其中每个变量a,b,c,d,e,f,g,h,i,j,k,l可以是除换行符之外的任何字符。
    var regex = /^(.{3}\.){3}.{3}$/;
    console.log(regex.test('123.456.abc.def'));
    // true
    console.log(regex.test('1243.446.abc.def'));
    // false
    console.log(regex.test('abc.def.ghi.jkl'));
    // true
    

    让我们分解一下,看看每步做了什么。

    1. 我们将整个正则表达式包装在^$内,以便匹配跨整个字符串。
    2. ( 第一个子表达式的开始。
    3. .{3} 与除换行符之外的任何字符完全匹配3次。
    4. \. 匹配字符点.
    5. ) 第一个子表达式的结尾。
    6. {3} 与第一个子表达式完全匹配3次。
    7. .{3} 与除换行符之外的任何字符完全匹配3次。

总结

正则表达式有时可能会相当复杂,但是对上述概念的正确理解将有助于你轻松理解更复杂的正则表达式模式。 你可以在这里了解有关正则表达式的更多信息,在这里练习。


原文链接🔗