《JavaScript权威指南》(犀牛书) 第十章 第一节
具有特殊含义的标点符号
^$.*+?=!:|\/()[]{}
注意:
- 如果想在正则表达式中使用这些字符的直接量进行匹配,则必须使用
\。 - 许多字母和数字在有反斜杠做前缀时也有特殊含义,所以对于数字和字母尽量不使用反斜杠转义。
一、直接量
const reg = /abc/;
const str = "a";
reg.test(str);
// false
const str1 = "abc";
reg.test(str1);
// true
🌈正则表达式中的直接量字符:
| 字符 | 匹配 |
|---|---|
| 字母和数字 | 自身 |
| \o | NUL字符(\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
六、修饰符
🌈正则表达式修饰符
| 字符 | 含义 |
|---|---|
| i | 执行不区分大小写的匹配 |
| g | 执行一个全局匹配,即找到所有的匹配,而不是在找到第一个之后就停止 |
| m | 多行匹配模式,^ 匹配一行的开头和字符串的开头,$ 匹配行的结束和字符串的结束 |