正则表达式(一)

161 阅读2分钟

《JavaScript权威指南》(犀牛书) 第十章 第一节

具有特殊含义的标点符号

^$.*+?=!:|\/()[]{}

注意:

  • 如果想在正则表达式中使用这些字符的直接量进行匹配,则必须使用 \
  • 许多字母和数字在有反斜杠做前缀时也有特殊含义,所以对于数字和字母尽量不使用反斜杠转义。

一、直接量

const reg = /abc/;

const str = "a";
reg.test(str);
// false

const str1 = "abc";
reg.test(str1);
// true

🌈正则表达式中的直接量字符:

字符匹配
字母和数字自身
\oNUL字符(\u0000)
\t制表符(\u0009)
\n换行符(\u000A)
\v垂直制表符(\u000B)
\f换页符(\u000C)
\r回车符(\u000D)

二、字符类: []

将直接量字符单独放进方括号内就组成了字符类。

一个字符类可以匹配它所包含的任意字符。

🌈正则表达式中的字符类:

字符匹配
[...]方括号内的任意字符
[^...]不在方括号内的任意字符
.除换行符和其他Unicode行终止符之外的任意字符
\w任何ASCII字符组成的单词,等价于 [a-zA-Z0-9]
\W任何不是ASCII字符组成的单词,等价于 [^a-zA-Z0-9]
\s任何Unicode空白符
\S任何非Unicode空白符,注意\w和\S不同
\d任何ASCII数字,等价于 [0-9]
\D除了ASCII数字之外的任何字符,等价于 [^0-9]
[\b]退格直接量,特例

注意:

点字符(.)匹配除回车(\r)、换行(\n) 、行分隔符(\u2028)和段分隔符(\u2029)以外的所有字符。注意,对于码点大于0xFFFF的 Unicode 字符,点字符不能正确匹配,会认为这是两个字符。

const reg = /[abc]/;

const str = "a";
reg.test(str);
// true

const str1 = "ab";
reg.test(str1);
// true

const str2 = "ac";
reg.test(str2);
// true

const str3 = "ad";
reg.test(str3);
// true

const str4 = "d";
reg.test(str4);
// false

只要有一个字符在字符类中,结果为 true

const reg = /[ab][cd]/;

const str = "ab";
reg.test(str);
// false

const str1 = "ac";
reg.test(str1);
// true

const str2 = "ca";
reg.test(str2);
// false

按顺序匹配。

否定字符类: ^

const reg = /[^abc]/;

const str = "a";
reg.test(str);
// false

const str1 = "d";
reg.test(str1);
// true

const str2 = "adc";
reg.test(str2);
// true

只要有一个字符不在字符类中,结果为 true

字符范围: -

const reg = /[a-z]/;

const str = "abc";
reg.test(str);
// true

const str1 = "abc0";
reg.test(str1);
// true

const str2 = "012";
reg.test(str2);
// false

const str3 = "012a";
reg.test(str3);
// true

只要有一个字符在字符类范围中,结果为 true

拓展:

字符编码笔记:ASCII,Unicode 和 UTF-8——阮一峰

三、重复

🌈正则表达式中的重复字符语法:

字符含义
{n,m}匹配前一项至少n次,但不能超过m次
{n,}匹配前一项n次或更多次
{n}匹配前一项n次
?匹配前一项0或1次,即前一项可选,等价于{0,1}
+匹配前一项1或多次,等价于{1,}
*匹配前一项0或多次,等价于{0,}

注意:

很多时候对于? + *可能记忆比较混乱,可以按下面方式记忆

  • 0 ? 1 :0次或者1次
  • 0 + 1 :0+1=1,1次或更多次
  • 0 * 1 :0*1=0,0次或更多次
/\d{2,4}/.test('1');
// false

/\d{2,4}/.test('12');
// true

/\d{2,4}/.test('1234567');
// true
'1234567'.match(/\d{2,4}/);
// [1234]

/A\d{2,4}B/.test('A1234567B');
// false

/\d{2,4}B/.test('A1234567B');
// true
'A1234567B'.match(/\d{2,4}B/);
// ['4567B']

非贪婪重复:?? *? +? {1,5}?

尽可能少地匹配

四、选择、分组和引用

选择:|

const reg = /ab|cd/;

const str = "ab";
reg.test(str);
// true

const str1 = "a";
reg.test(str1);
// false

const str2 = "c";
reg.test(str2);
// false

const str3 = "ac";
reg.test(str3);
// false

const str4 = "abc";
reg.test(str4);
// true
const reg = /(ab|cd)+|ef/;

const str = "e";
reg.test(str);
// false

const str1 = "ef";
reg.test(str1);
// true

const str2 = "abcd";
reg.test(str2);
// true

引用:\数字

带圆括号的表达式运行在同一表达式的后部引用前面的子表达式。

例如:\1 引用第一个带圆括号的字表达式

注意:因为字表达式可以嵌套另一个字表达式,所以它的位置是参与计数的左括号的位置。

const reg = /(['"])[^'"]*\1/;
const str1 = `'123"`;
reg.test(str1);
// false

const str2 = `'123'`;
reg.test(str2);
// true

在不使用 \1 引用的情况下,单双引号无法配对成功:

const reg = /(['"])[^'"]*(['"])/;
const str = `'123"`;
reg.test(str);
// true
const reg = /(['"])[^'"]*['"]/;
const str = `'123"`;
reg.test(str);
// true

分组:() (?:...)

const reg = /([Jj]ava([Ss]cript)?)\sis\s(fun\W*)/;
const str ="javascript is funny!";
reg.test(str);
// true
const reg = /([Jj]ava(?:[Ss]cript)?)\sis\s(fun\W*)/;
const str ="javascript is funny!";
reg.test(str);
// true

const str = "javascript is funnnny!";
reg.test(str);
// true 前面javascript is fun已经匹配成功,所以为true

(?:...) 这种改进的圆括号并不生成引用,所以在 /([Jj]ava(?:[Ss]cript)?)\sis\s(fun\W*)/ 这个正则表达式中,\2 引用了与 (fun\W*) 匹配的文本。

const reg = /([Jj]ava(?:[Ss]cript)?)\sis\s(fun\W*)\2/;
const str = "javascript is fun!fun!";
reg.test(str);
// true

引用的是已匹配的文本,而不是正则表达式。

🌈正则表达式的选择、分组和引用字符:

字符含义
|选择,匹配该符号左边或右边的字表达式
(...)组合,将几个项组合为一个单元,这个单元可通过 * + ? `` 等符号加以修饰,而且可以记住和这个组合相匹配的字符串以供以后的引用使用
(?:...)只组合,把项组合到一个单元,但不记忆与该组相匹配的字符
\n和第n个分组第一次匹配的字符相匹配,组是圆括号中的字表达式(可能是嵌套的),组索引是从左到右的左括号数,"(?:)"形式的分组不编码

五、指定匹配位置

🌈正则表达式中的锚元素:

字符含义
^匹配字符串的开头,在多行检索中,匹配一行的开头
$匹配字符串的结尾,在多行检索中,匹配一行的结尾
\b匹配一个单词的边界,就是位于字符 \w\W 的位置,或位于字符 \w 和字符串的开头或结尾之间的位置。
注意:[\b] 匹配的是退格符
\B匹配非单词边界的位置
x(?=y)零宽正向先行断言
x(?!y)零宽负向先行断言

匹配开头和结尾:^ $

const reg = /^[Hh]ello\s[wW]orld\!$/;
const str = "hello world!";
reg.test(str);
// true

const reg = /^[Hh]ello\s[wW]orld\!$/;
const str = "hello world";
reg.test(str);
// false

const reg = /^[Hh]ello\s[wW]orld\!$/;
const str = "aello world!";
reg.test(str);
// false

匹配词的边界:\b \B

const reg = /hello\bworld/;
const str = "hello world";
reg.test(str);
// false

const reg = /hello\sworld/;
const str = "hello world";
reg.test(str);
// true

const reg = /\bworld/;
const str = "hello world";
reg.test(str);
// true

const reg = /\bworld/;
const str = "hello-world";
reg.test(str);
// true

const reg = /\bworld/;
const str = "helloworld";
reg.test(str);
// false
const reg = /\B[Ss]cript/;
const str = "javaScript";
reg.test(str);
// true

const reg = /\B[Ss]cript/;
const str = "Script";
reg.test(str);
// false

const reg = /\B[Ss]cript/;
const str = "java Script";
reg.test(str);
// false

const reg = /\B[Ss]cript/;
const str = "java-Script";
reg.test(str);
// false

先行断言:(?=) (?!)

const reg = /[Jj]ava(Script)?(?=\:)/;
const str = "JavaScript: abcd";
reg.test(str);
// true

const reg = /[Jj]ava(Script)?(?=\:)/;
const str = "Java";
reg.test(str);
// false

const reg = /[Jj]ava(Script)?(?=\:)/;
const str = "Java: abc";
reg.test(str);
// true
const reg = /Java(?!Script)([A-Z]\w*)/;
const str = "JavaBeans";
reg.test(str);
// true

const reg = /Java(?!Script)([A-Z]\w*)/;
const str = "Javanese";
reg.test(str);
// false

const reg = /Java(?!Script)([A-Z]\w*)/;
const str = "JavaScript";
reg.test(str);
// false 书中说法错误

const reg = /Java(?!Script)([A-Z]\w*)/;
const str = "JavaScripter";
reg.test(str);
// false

javascript权威指南里正则表达式这有点问题

六、修饰符

🌈正则表达式修饰符

字符含义
i执行不区分大小写的匹配
g执行一个全局匹配,即找到所有的匹配,而不是在找到第一个之后就停止
m多行匹配模式,^ 匹配一行的开头和字符串的开头,$ 匹配行的结束和字符串的结束