Rashomon511/MyBlog

正则表达式学习总结(四)

Opened this issue · 0 comments

正则表达式的构建

使用正则的三个前提:是否能使用正则? 是否有必要使用正则? 是否有必要构建一个复杂的正则?
在回答完以上三个问题后再考虑使用正则表达式

平衡法则

• 匹配预期的字符串
• 不匹配非预期的字符串
• 可读性和可维护性
• 效率

示例:
匹配固定电话:

let regex = /^(0\d{2,3}-?|\(0\d{2,3}\))[1-9]\d{6,7}$/

匹配浮点数

let regex = /^[+-]?(\d+\.\d+|\d+|\.\d+)$/

正则表达式的运行分为如下的阶段

    1. 编译;
    1. 设定起始位置;
    1. 尝试匹配;
    1. 匹配失败的话,从下一位开始继续第 3 步;
    1. 最终结果:匹配成功或失败。

提升效率的方法:

  • 可以使用具体型字符组来代替通配符,来消除回溯
    • 匹配字符串 123"abc"456 中的 "abc",/"."/可以修改为/"[^"]"/
  • 使用非捕获型分组
    • /^[-]?(\d.\d+|\d+|.\d+)$/ 可以修改成:/^[-]?(?:\d.\d+|\d+|.\d+)$/
  • 独立出确定字符
    • /a+/ 可以修改成 /aa*/
  • 提取分支公共部分
    • 比如,/^abc|^def/ 修改成 /^(?:abc|def)/。 又比如, /this|that/修改成 /th(?:is|at)/
  • 减少分支的数量,缩小它们的范围
    • /red|read/ 可以修改成 /rea?d/

正则表达式编程

正则表达式的四种操作: 验证、切分、提取、替换

  • 验证:有没有匹配,是不是匹配上,判断是否的操作,即称为“验证”
  • 切分:就是把目标字符串,切成一段一段的
  • 提取: 通常要使用分组引用(分组捕获)功能,提取部分匹配的数据
  • 替换:在 JavaScript 中,使用 replace 进行替换

正则操作的方法

字符串实例 4 个,正则实例 2 个

  • String#search
  • String#split
  • String#match
  • String#replace
  • RegExp#test
  • RegExp#exec

search 和 match 的参数问题

search 和 match,会把字符串转换为正则的

var string = "2017.06.27";
// 效果相同
console.log( string.search("\\.") );  // 4
console.log( string.search(/\./) ); //4
// 效果相同
console.log( string.match("\\.") );  // [".", index: 4, input: "2017.06.27"]
console.log( string.match(/\./) );  // [".", index: 4, input: "2017.06.27"]

match 返回结果的格式问题

没有 g,返回的是标准匹配格式,即,数组的第一个元素是整体匹配的内容,接下来是分组捕获的内容,然 后是整体匹配的第一个下标,最后是输入的目标字符串,有 g,返回的是所有匹配的内容,没有匹配时,不管有无 g,都返回 null

var string = "2017.06.27";
  var regex1 = /\b(\d+)\b/;
  var regex2 = /\b(\d+)\b/g;
  console.log( string.match(regex1) );
  console.log( string.match(regex2) );
  // => ["2017", "2017", index: 0, input: "2017.06.27"]
  // => ["2017", "06", "27"]

exec 比 match 更强大

exec 方法可以解决,正则没有 g 时使用 match 返回的信息比较多。但是有 g 后,就没有关键的信息 index 带来的问题。exec 方法可以接着上一次匹配后继续匹配

修饰符 g,对 exex 和 test 的影响

有g时都是从 lastIndex位置开始匹配,匹配成功后每次都会修改 lastIndex。没有 g,自然都是从字符串第 0 个字符处开始尝试匹配

test 整体匹配时需要使用 ^ 和 $

整体匹配,正则前后需要添加开头和结尾

console.log( /123/.test("a123b") );
  // => true
  console.log( /^123$/.test("a123b") );
  // => false
  console.log( /^123$/.test("123") );
  // => true

使用构造函数需要注意的问题

一般不推荐使用构造函数生成正则,而应该优先使用字面量。因为用构造函数会多写很多 \

修饰符

  • g:全局匹配,即找到所有匹配的,单词是 global。
  • i: 忽略字母大小写,单词是 ingoreCase。
  • m :多行匹配,只影响 ^ 和 $,二者变成行的概念,即行开头和行结尾。单词是 multiline。

构造函数属性

  • RegExp.input | 最近一次目标字符串 | RegExp["$_"]
  • RegExp.lastMatch | 最近一次匹配的文本 | RegExp["$&"]
  • RegExp.lastParen | 最近一次捕获的文本 | RegExp["$+"]
  • RegExp.leftContext | 目标字符串中lastMatch之前的文本 | RegExp["$`"]
  • RegExp.rightContext | 目标字符串中lastMatch之后的文本 | RegExp["$'"]

示例:
使用构造函数生成正则表达式

 <p class="high">1111</p>
  <p class="high">2222</p>
  <p>3333</p>
  <script>
  function getElementsByClassName (className) {
      var elements = document.getElementsByTagName("*");
      var regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
      var result = [];
      for (var i = 0; i < elements.length; i++) {
          var element = elements[i];
          if (regex.test(element.className)) {
              result.push(element)
          }
}
      return result;
  }
  var highs = getElementsByClassName('high');
  highs.forEach(function (item) {
      item.style.color = 'red';
  });
</script>

使用字符串保存数据

var utils = {};
  "Boolean|Number|String|Function|Array|Date|RegExp|Object|Error".split("|").forEach(fun
  ction (item) {
      utils["is" + item] = function (obj) {
          return {}.toString.call(obj) == "[object " + item + "]";
}; });
  console.log( utils.isArray([1, 2, 3]) );
  // => true