字符匹配
模糊匹配
- 横向模糊匹配: 一个正则可匹配的字符串的长度不是固定的,可以是多种情况的。
- 纵向模糊匹配: 一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符,可以有多种可能。
// 横向
var regex = /ab{2,5}c/g;
var string = "abc abbc abbbc abbbbc abbbbbc abbbbbbc";
console.log( string.match(regex) );
// => ["abbc", "abbbc", "abbbbc", "abbbbbc"]
// 纵向
var regex = /a[123]b/g;
var string = "a0b a1b a2b a3b a4b";
console.log( string.match(regex) );
// => ["a1b", "a2b", "a3b"]
字符组
- 范围表示
- 排除字符组
- 简写
\d就是[0-9]。表示是一位数字。记忆方式:其英文是digit(数字)。
\D就是[^0-9]。表示除数字外的任意字符。
\w就是[0-9a-zA-Z_]。表示数字、大小写字母和下划线。记忆方式:w是word的简写,也称单词字符。
\W是[^0-9a-zA-Z_]。非单词字符。
\s是[ \t\v\n\r\f]。表示空白符,包括空格、水平制表符、垂直制表符、换行符、回车符、换页符。记忆方式:s是space character的首字母。
\S是[^ \t\v\n\r\f]。非空白符。
.就是[^\n\r\u2028\u2029]。通配符,表示几乎任意字符。换行符、回车符、行分隔符和段分隔符除外。记忆方式:想想省略号…中的每个点,都可以理解成占位符,表示任何类似的东西。
// 范围
const reg = /[123456abcdefGHIJKLM]/;
// 排除
const reg = /[^abc]/;
// 简写
const reg = /\d\w\s./
量词
- {m,n}
- {m,} 表示至少出现m次。
- {m} 等价于{m,m},表示出现m次。
- ? 等价于{0,1},表示出现或者不出现。记忆方式:问号的意思表示,有吗?
- 等价于{1,},表示出现至少一次。记忆方式:加号是追加的意思,得先有一个,然后才考虑追加。
- 等价于{0,},表示出现任意次,有可能不出现。记忆方式:看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。
- 贪婪匹配和惰性匹配
// 贪婪模式
var regex = /\d{2,5}/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["123", "1234", "12345", "12345"]
// 修改为惰性
var regex = /\d{2,5}?/g;
var string = "123 1234 12345 123456";
console.log( string.match(regex) );
// => ["12", "12", "34", "12", "34", "12", "34", "56"]
多选分支
分支结构也是惰性的,即当前面的匹配上了,后面的就不再尝试了。
var regex = /good|nice/g;
var string = "good idea, nice try.";
console.log( string.match(regex) );
// => ["good", "nice"]
var regex = /good|goodbye/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["good"]
var regex = /goodbye|good/g;
var string = "goodbye";
console.log( string.match(regex) );
// => ["goodbye"]
匹配位置
(?=p) 匹配p前面位置
(?!p) 匹配非p前面的位置
var result = "hello".replace(/(?=l)/g, '#');
console.log(result);
// => "he#l#lo"
var result = "hello".replace(/(?!l)/g, '#');
console.log(result);
// => "#h#ell#o#"
// 匹配同时包含小写、大写、数字
// 分解如下:
// 1. 同时包含数字和小写字母
// 2. 同时包含数字和大写字母
// 3. 同时包含小写字母和大写字母
// 4. 同时包含数字、小写字母和大写字母
var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9A-Za-z]{6,12}$/;
console.log( reg.test("1234567") ); // false 全是数字
console.log( reg.test("abcdef") ); // false 全是小写字母
console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
console.log( reg.test("ab23C") ); // false 不足6位
console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
console.log( reg.test("abcdEF234") ); // true
// 另外一种解法: “至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。
var reg = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/;
console.log( reg.test("1234567") ); // false 全是数字
console.log( reg.test("abcdef") ); // false 全是小写字母
console.log( reg.test("ABCDEFGH") ); // false 全是大写字母
console.log( reg.test("ab23C") ); // false 不足6位
console.log( reg.test("ABCDEF234") ); // true 大写字母和数字
console.log( reg.test("abcdEF234") ); // true
\b 单词边界
- \w 与 \W 之间
- \w 与 ^ 之间
- \w 与 $ 之间
\B 非单词边界
- \w 与 \w
- \W 与 \W
- \W 与 $
- \W 与 ^
var result = "[JS] Lesson_01.mp4".replace(/\b/g, '#');
console.log(result);
// => "[#JS#] #Lesson_01#.#mp4#"
var result = "[JS] Lesson_01.mp4".replace(/\B/g, '#');
console.log(result);
// => "#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4"
位置特性
字符之间的位置,可以写成多个, 可理解为空字符串
"hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "o" + "";
"hello" == "" + "" + "hello"
var result = /^^hello?$/.test("hello");
console.log(result);
// => true
括号的作用
分组
// 使量词 + 作用于整体
var regex = /(ab)+/g;
var string = "ababa abbb ababab";
console.log( string.match(regex) );
// => ["abab", "ab", "ababab"]
// 支持子表达式
var regex = /^I love (JavaScript|Regular Expression)$/;
console.log( regex.test("I love JavaScript") );
console.log( regex.test("I love Regular Expression") );
// => true
// => true
使用分组
// 提取数据
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log( string.match(regex) );
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]
// 替换数据
var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
var result = string.replace(regex, function() {
return RegExp.$2 + "/" + RegExp.$3 + "/" + RegExp.$1;
});
console.log(result);
// => "06/12/2017"
// 反向引用
var regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/;
var string1 = "2017-06-12";
var string2 = "2017/06/12";
var string3 = "2017.06.12";
var string4 = "2016-06/12";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // true
console.log( regex.test(string4) ); // true
// 前后间隔符保持一致,需要反向引用匹配的第一个分隔符
var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;
console.log( regex.test(string4) ); // false
// 匹配成对标签
var regex = /<([^>]+)>[\d\D]*<\/\1>/;
var string1 = "<title>regular expression</title>";
var string2 = "<p>laoyao bye bye</p>";
var string3 = "<title>wrong!</p>";
console.log( regex.test(string1) ); // true
console.log( regex.test(string2) ); // true
console.log( regex.test(string3) ); // false
嵌套括号
根据开括号从左往右依次分组
var regex = /^((\d)(\d(\d)))\1\2\3\4$/;
var string = "1231231233";
console.log( regex.test(string) ); // true
console.log( RegExp.$1 ); // 123
console.log( RegExp.$2 ); // 1
console.log( RegExp.$3 ); // 23
console.log( RegExp.$4 ); // 3
\10代表第十个分组,而非\1和0的组合
var regex = /(1)(2)(3)(4)(5)(6)(7)(8)(9)(#) \10+/;
var string = "123456789# ######"
console.log( regex.test(string) );
// => true
不存在引用分组,则代表字符本身
var regex = /\1\2\3\4\5\6\7\8\9/;
console.log( regex.test("\1\2\3\4\5\6\7\8\9") ); // true
非捕获分组
如果只想要括号最原始的功能,但不会引用它,即,既不在API里引用,也不在正则里反向引用。此时可以使用非捕获分组(?:p)
var regex = /(?:ab)+/g;
var string = "ababa abbb ababab";
console.log( string.match(regex) );
// => ["abab", "ab", "ababab"]
正则表达式操作符优先级
- 1.转义符 \
- 2.括号和方括号 (…)、(?:…)、(?=…)、(?!…)、[…]
- 3.量词限定符 {m}、{m,n}、{m,}、?、*、+
- 4.位置和序列 ^ 、$、 \元字符、 一般字符
- 管道符(竖杠)|
var reg = /ab?(c|de*)+|fg/
// 由于括号的存在,所以,(c|de*)是一个整体结构。
// 在(c|de*)中,注意其中的量词*,因此e*是一个整体结构。
// 又因为分支结构“|”优先级最低,因此c是一个整体、而de*是另一个整体。
// 同理,整个正则分成了a、b?、(...)+、f、g。而由于分支的原因,又可以分成ab?(c|de*)+和fg这两部分。。
修饰符
g 全局匹配,即找到所有匹配的,单词是global
i 忽略字母大小写,单词ingoreCase
m 多行匹配,只影响^和$,二者变成行的概念,即行开头和行结尾。单词是multiline