JavaScript 正则表达式
转载: https://www.cnblogs.com/rubylouvre/archive/2010/03/09/1681222.html
https://www.cnblogs.com/tugenhua0707/p/5037811.html#_labelTop
忽略优先与匹配优先量词
匹配优先量词(也有叫做贪婪量词): 匹配尽可能多的字符;
在一个正则表达式中, 假定为 /\d+3/, 字符 a123456ab
会先从 \d 开始匹配, 在字符 1处满足, 然后逐个匹配到字符6的位置; 再去匹配表达式 3, 字符 a不满足; 再从上一次的结果 123456中去匹配, 6, 5, 4, 3 找到满足的匹配; 最终返回 123的结果;
借用引用博文中的话: 一口吞下整个字符串,发现吞不下(匹配不了),再从后面一点点吐出来(去掉最后一个字符,再看这时这个整个字符串是否匹配,不断这样重复直到长度为零);
忽略优先量词(也有叫做惰性量词, 在量词后面加 ?, 如 /a+?/):
书中(正则指引)原话: `忽略优先量词会选择"不匹配"的状态, 再尝试表达式中之后的元素,如果尝试失败, 再回溯, 选择之前保存的"匹配"的状态`
假定为 /\d+?3/, 字符 a123456ab
意即: 先不匹配表达式 \d+?, 再尝试表达式 3, 匹配字符 a失败, 回溯,由 \d 来匹配, 不符合;
重新尝试匹配表达式 3, 匹配字符 1失败, 回溯, 由 \d 匹配, 符合, 匹配状态更新为 1;
继续匹配表达式 3, 匹配字符 2失败, 回溯, 由 \d 匹配, 符合, 匹配状态更新为 12;
继续匹配表达式 3, 匹配字符 3成功, 由于 \d+ 语义至少一个字符, 因此从匹配状态中返回12, 最终返回 123;
关于忽略优先, 再来看一看下面的几个例子;
var pa1 = /\d+?2/;
var pa2 = /\d{2,4}?2/
var pa3 = /2\d{2,4}?/
var str = '12345672222';
console.log(pa1.exec(str)); // 12
console.log(pa2.exec(str)); // 45672
console.log(pa3.exec(str)); // 234
关于第二个未能理解清楚不作分析, 这与大多数文章中提到的"忽略优先匹配尽可能少的字符"并不一致;留个问题...
环视(也有称作零宽断言)
“停在原地, 四处张望”, 在它旁边的文本需要满足某种条件, 而且本身不匹配任何字符
肯定顺序环视: (?=...) , 匹配后面的字符满足表达式 ...
否定顺序环视: (?!...) , 匹配后面的字符不满足表达式 ...
肯定逆序环视(ES6支持了肯定逆序环视): (?<=...) , 匹配前面的字符满足表达式 ...
否定逆序环视(ES6支持了否定逆序环视): (?<!...) , 匹配前面的字符不满足表达式 ...
比如, 匹配以 http/https 开头, 且以 ? 为结尾的中间字符串
var str = '这是一个链接: https://cn.bing.com/search?cvid=2FCA483B08F94EEFA257952193CB520D&first=11&FORM=PERE;';
var pa = /(?<=https?:\/\/)[^?]+(?=\?)/;
console.log(pa.exec(str)); // cn.bing.com/search
(?<=https?:\/\/) 断言以 http(s):// 开头;
(?=\?) 断言以 ? 结尾;
否定环视在执行判断时取反, 匹配左侧或右侧的字符不满足表达式;并且环视内的表达式可以并列(组合的情况测试不支持);
比如:
var str2 = 'abs1239a2501aasbafs';
var pa2 = /\w+((?=[0-9])|(?=a))/;
console.log(pa2.exec(str2)); //abs1239a2501aasb
需要注意的是, 环视并不会改变匹配位置; 比如上面的
var str2 = 'abs1239a2501aasbafs';
var pa2 = /[a-z]+(?=[0-9])([0-9]+)/;
console.log(pa2.exec(str2)) // $1=1239
匹配 [0-9]+ 这个捕获组时, 仍旧是从 1出现的位置开始匹配; 因为环视只是做前后的判断并不会影响匹配的位置,但如果在环视结构里面写了括号, 比如 (?=(a|b)), 这种操作会改变分组编号且不能回溯, 这很可能会导致问题,因此不建议在环视结构中使用捕获;
匹配模式
常用的匹配模式有忽略大小写模式, 单行模式, 多行模式, ES6中新增粘性模式(简写 y), Unicode 模式 (简写 u);
这里只介绍下单行和多行模式, Unicode 模式尚未理解;
先来说说行: 可以简单理解为 行起始位置到行结束位置中的内容为单独一行; 行起始位置, 可以认为是起始位置, 或是行结束位置的下一位置; 行结束位置由一个行终止符作为标识;
行终止符在不同系统平台下略有不同:
平台 行终止符
Unix/Linex \n
Windows \r\n
MacOs \n
如果是多行模式(m), 则截至行终止符为一行, 并将 ^ 和 $设为字符串内部某一行的起始位置和结束位置, 对行内文本分别执行匹配; 并且点号 . 匹配的除了换行符以外的所有字符;
如果是单行模式(s), 所有字符都只在一行里, ^ 和 $ 匹配的是整个字符串的起始位置和结束位置, 并且点号 . 可以匹配包括换行符在内的所有字符;
比如, 要处理一段文字, 要求对每行文本包裹到一个 p标签中, 并剔除行内中文之间的空格, 剔除行内首尾的空格;
var str = ' 2019: 1000元, 明 细: 2元人名币 干啥字 OO;\n 2020: 1000元, 明细: 搞嗷嗷 撒地方 http://baidu.com; \n 2021: 1000元, 明细: 搞嗷嗷 撒地方 \nhttp://baidu.com ';
var pa = /(?<=[\u4e00-\u9fa5])\s+(?=[\u4e00-\u9fa5])/gm; // 剔除所有行内, 中文之间的空格
var pa1 = /^\s+/mg // 剔除所有行内, 首空格
var pa2 = /\s+$/mg // 剔除所有行内, 尾空格
var str2 = str.replace(pa, '').replace(pa1, '').replace(pa2, '')
console.log(str2.replace(/^([\d\D]+?)$/gm, '<span>$1</span>')) // 对所有行首尾出增加 span的闭合标签
错误之处欢迎指正。
附: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes