跳到主要内容

正则表达式

概念

正则表达式在我们日常的软件开发过程中被广泛使用,例如编写配置文件、在 Linux 与 macOS 下查找文件,操作模版文件,匹配 URL,数据验证等,然而不同软件不同操作系统对于正则的应用有着不一样的行为,主要原因是正则表达式演进过程中,出现 POSIX 与 PCRE 派系之分。

90 年代,随着 Perl 语言的发展,它的正则表达式功能越来越强悍。为了把 Perl 语言中正则的功能移植到其他语言中, PCRE (Perl Compatible Regular Expressions)派系的正则表达式也诞生了。现代编程语言如 Python , Ruby , PHP , C / C++ , Java,Javascript 等正则表达式,大部分都属于 PCRE 派系。支持格式:/regular expression/switch。

注:Perl 语言是一种擅长处理文本的语言,语法晦涩,符号古怪,不利于理解和记忆导致很多开发者并不喜欢 https://www.perl.org/

什么是正则表达式 RegExp

在 mdn 上的解释正则表达式是用于匹配字符串中字符组合的模式。在 JavaScript 中,正则表达式也是对象。

这些模式被用于 RegExp 的

  • exec
  • test

以及 String 的

  • match
  • matchAll
  • replace
  • search
  • split
  • replaceAll 方法

创建方式

1.字面量

由斜杠(/)包围而不是引号包围 /pattern/flags

/ab+c/i; // 正则字面量

2.构造函数

由引号包围,不包含在斜杠之间 new RegExp(pattern[, flags])

new RegExp("ab+c", "i"); // 字符串形式
new RegExp(/ab+c/, "i"); // ES6加强,构造函数参数也可以直接传正则字面量
// 这样就不用转义了

3.工厂方法

RegExp(pattern[, flags])

跟构造函数区别不大

字面量形式与构造函数形式的取舍

字面量形式提供正则表达式的编译状态,脚本加载后,正则表达式字面量就会被编译。当正则表达式保持不变时,使用此方法可获得更好的性能。

正则表达式构造函数形式,提供了运行时编译,例如,在循环中,循环一次就会编译一次。如果正则表达式会改变,或者 pattern 从其他来源获取(如用户输入)请使用构造函数。

function testRegExp(input, pattern) {
const regExp = new RegExp(`${pattern}123`);
return regExp.test(input);
}
testRegExp("hello123", "hello"); // true

字符串写法需要转义

在书写正则表达式时,我们会用到 [ ] / \ + * . $^ | ?- 这些特殊字符 ,具有特殊意义。为了匹配这些特殊字符本身,我们需要通过\将它们转义。 当使用构造函数创造正则对象时,需要用常规的字符转义规则(在前面加反斜杠\) 举例: 字面量 字符串

/\[abc\]d/  "\\[abc\\]d"
/\.ab/ "\\.ab"
/\w+/ "\\w+"

字符串写法/换成“但,特殊符号前需要\转义
/[abc]d/.test("ad"); //true
/\[abc\]d/.test("ad"); //false,只会匹配[abc]d
/\[abc\]d/.test("[abc]d"); //true
RegExp("\\[abc\\]d").test("[abc]d"); //true

//通过console输出查看, 转义pattern是否一致方法
console.log(/\w+/);
console.log(new RegExp("\\w+"));

console.log(/\w+/.toString()); // RegExp.prototype.toString()
console.log(new RegExp("\\w+").toString());

console.log(/\w+/.source); // RegExp.prototype.source
console.log(new RegExp("\\w+").source);

flags

flags 包含要添加的标志的字符串

  • g 全局匹配:找到所有的匹配,而不是在第一个匹配后停止
  • i 忽略大小写
  • m 多行匹配,将开始和结束字符(^ $)视为在多行上工作。换句话说,匹配每一行的开头和结尾(由\n 或\r 分隔),而不仅仅是整个输入字符串的开头和结尾, 这样 breakwords 不会被识别
  • y 粘性匹配(sticky),仅匹配 lastIndex 之后的字符串
  • s 开启点号匹配所有字符,甚至允许. 去匹配行结束符(这往往在文本处理会被忽略)
  • u 使用 unicode 码的模式进行匹配。

由于 lastIndex 导致匹配失败的坑点

如果指定 flags,构造函数 pattern 是一个正则对象,flags 字符串将会替换该对象的任何标志,并且 lastIndex 将重置为 0(ES6 开始) 如果没有指定 flags,构造函数 pattern 是一个正则对象,则复制该对象的 flags 包括 lastIndex。

// lastIndex test
const reg = /abc/g;
reg.test("abc"); // true lastIndex=3
RegExp(reg).test("01abc"); // false,因为 lastIndex 未重置为0,同时匹配失败后 lastIndex 重置为0
// RegExp(reg, 'g').test('01abc'); //true,因为lastIndex 重置为0,同时匹配成功后 lastIndex为新的

String.match(Exp)

匹配单词

  // i want to
let wordList = article.match(/[a-z]+/g),//["i","want","to"]
// 或者

let wordList = article.match(/\b\w+\b/g),//["i","want","to"]

但是这匹配不了‘over-all‘

可以改成这样

let wordList = article.match(/\b\w+(?:-\w+)*\b/g);