正则表达式的简单介绍

 一家之言  原创  管理员  2018-12-04 20:49

概要:1951 年,一位名叫Stephen Kleene的数学科学家,在Warren McCulloch和Walter Pitts早期工作的基础之上,发表了一篇题目是《神经网事件的表示法》的论文,利用称之为正则集合的数学符号来描述此模型,引入了正则表达式的概念。正则表达式被作为用来描述其称之为“正则集的代数”的一种表达式,因而采用了“正则表达式”这个术语。

 

是什么?


1、对字符串操作的一种逻辑公式


2、用特定字符组成的“规则字符串”


3、这个“规则字符串”用来表达对字符串的过滤逻辑


从哪里来?


1951 年,一位名叫Stephen Kleene的数学科学家,在Warren McCulloch和Walter Pitts早期工作的基础之上,发表了一篇题目是《神经网事件的表示法》的论文,利用称之为正则集合的数学符号来描述此模型,引入了正则表达式的概念


正则表达式被作为用来描述其称之为“正则集的代数”的一种表达式,因而采用了“正则表达式”这个术语


大名鼎鼎的Unix之父Ken Thompson把这一成果应用于计算搜索算法的一些早期研究


Unix之父将此符号系统引入编辑器QED,然后是Unix上的编辑器ed,并最终引入grep


自此以后,正则表达式被广泛地应用到各种UNIX或类似于UNIX的工具中,如Perl


有什么目的?


  • 判断一个字符串是否符合某一规则
  • 从某一字符串中提取指定的部分

正则引擎


DFA

确定性有穷自动机(Deterministic Finite Automaton)

在线性时状态下执行,不要求回溯

程序语言:MySQL,Procmail...


NFA

非确定性有穷自动机(Non-deterministic Finite Automaton)

运行回溯算法

程序语言:Java,.NET,Perl,PHP,Python,Ruby...


区别示例

字符串:this is yansen’s blog

正则:ya(msen|nsen|nsem)


DFA会从 this 中 t 开始依次查找 y,定位到 y ,已知其后为 a ,则查看表达式是否有 a ,此处正好有 a 。然后字符串 a 后为 n ,DFA依次测试表达式,此时 msen 不符合要求淘汰。nsen 和 nsem 符合要求,然后DFA依次检查字符串,检测到sen 中的 n 时只有nsen 分支符合,则匹配成功!


NFA工作方式如下,先在字符串中查找 y 然后匹配其后是否为 a ,如果是 a 则继续,查找其后是否为 m 如果不是则匹配其后是否为 n (此时淘汰msen选择支)。然后继续看其后是否依次为 s,e,接着测试是否为 n ,是 n 则匹配成功,不是则测试是否为 m 。为什么是 m ?因为 NFA 工作方式是以正则表达式为标准,反复测试字符串,这样同样一个字符串有可能被反复测试了很多次!


语法


匹配字符

.

匹配除 "\n" 之外的任何单个字符

\d

匹配一个数字字符

\D

匹配一个非数字字符


\n

匹配一个换行符

\r

匹配一个回车符

\s

匹配任何空白字符,包括空格、制表符、换页符等


\t

匹配一个制表符

\w

匹配包括下划线的任何单词字符

\un

匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符


匹配位置

^

匹配开头的位置,多行模式下为每行的开头位置

$

匹配结束的位置,多行模式下为每行的结束位置


\b

匹配一个单词边界

(?=p)

匹配p前面的位置

(?!p)

匹配后面跟的不是p的位置


量词

  •  

匹配前面的子表达式零次或多次

  •  

匹配前面的子表达式一次或多次

?

匹配前面的子表达式零次或一次


{n}

匹配确定的 n 次

{n,}

至少匹配n 次

{n,m}

最少匹配 n 次且最多匹配 m 次


贪婪匹配

尽可能匹配多的字符

let str="123456789abcde";
let reg=str.match(/^\d+/);
console.log(reg);
//["123456789", index: 0, input: "123456789abcde"]

懒惰匹配

尽可能匹配少的字符

let str="123456789abcde";
let reg=str.match(/^\d+?/);
console.log(reg);
//["1", index: 0, input: "123456789abcde"]

分支

  • 可以理解为逻辑或,格式为:p1|p2|p3
  • 优先级最低

示例分析

var regex = /^([01][0-9]|[2][0-3]):[0-5][0-9]$/;
console.log( regex.test("23:59") );
console.log( regex.test("02:07") );
// => true
// => true
var regex = /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/;
console.log( regex.test("2017-06-10") );
// => true

位置匹配


什么是位置?

H e l l o , W o r l d


^与$

var result = "hello".replace(/^|$/g, '#');
console.log(result);
// => "#hello#"

(?=p)

let result = "hello".replace(/(?=[\w])/g, '#');
console.log(result); 
//#h#e#l#l#o

\b

let result = "hello world".replace(/\b/g, '#');
console.log(result); 
//#hello# #world#

数字千位表示

var regex = /(?!^)(?=(\d{3})+$)/g;
var result = "12345678".replace(regex, ',')
console.log(result);
// => "12,345,678"


result = "123456789".replace(regex, ',');
console.log(result);
// => "123,456,789"

分组


示例:

var regex = /\d{4}-\d{2}-\d{2}/;


var regex = /(\d{4})-(\d{2})-(\d{2})/;

提取数据:

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"]

反向引用


写一个正则支持匹配如下三种格式:

2018-06-12
2018/06/12
2018.06.12

var regex = /\d{4}(-|\/|\.)\d{2}(-|\/|\.)\d{2}/;
var regex = /\d{4}(-|\/|\.)\d{2}\1\d{2}/;

非捕获匹配


  • 之前文中出现的括号,都会捕获它们匹配到的数据,以便后续引用,因此也称它们是捕获型分组和捕获型分支

  • 如果只想要括号最原始的功能,但不会引用它,即:既不在 API 里引用,也不在正则里反向引用。此时可以使用非捕获括号 (?:p) 和 (?:p1|p2|p3)


回溯





正则的构建


平衡法则

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

  • 是否可以使用正则来实现?
  • 是否有必要?
  • 是否过于复杂?
  • 性能是否满足要求?

性能优化


  • 使用具体型字符组来代替通配符,来消除回溯
  • 使用非捕获型分组
  • 独立出确定字符
  • 提取分支公共部分
  • 减少分支的数量,缩小它们的范围


其它


单行注释
(?#comment)

多行注释
      (?<=    # 断言要匹配的文本的前缀
      <(\w+)> # 查找尖括号括起来的字母或数字(即HTML/XML标签)
      )       # 前缀结束
      .*      # 匹配任意文本
      (?=     # 断言要匹配的文本的后缀
      <\/\1>  # 查找尖括号括起来的内容:前面是一个"/",后面是先前捕获的标签
      )       # 后缀结束

平衡组
(?'group') 把捕获的内容命名为group,并压入堆栈(Stack)
(?'-group') 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败

(?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分
(?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败

括号匹配示例
<                         
    [^<>]*                
    (
        (
            (?'Open'<)    
            [^<>]*       
        )+
        (
            (?'-Open'>)   
            [^<>]*        
        )+
    )*
    (?(Open)(?!))         

>                         

THE END

正则表达式  

编辑:myweb   最后更新于:2019-03-28 20:51

点评:不要怕,上手非常简单。



声明:本站部分文章系本站编辑转载,转载目的在于加快信息的传递,及时与广大网友分享更多信息,并不代表本站赞同其观点和对其真实性负责。 如涉及作品内容、版权和其它问题,请及时与本站联系,我们将在第一时间删除内容!本站文章版权归原作者所有,内容为作者个人观点,本站只提供参考并不构成任何投资及应用的建议。


联系我:x889@foxmail.com,鄂ICP备14016278号-2
©2016-2019 我的ABC All Rights Reserved.
友情链接: 一起编程网