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

posted @ 2021-02-18 15:45  芋头圆  阅读(100)  评论(0编辑  收藏  举报