到处逛博客想找正则优化的办法,发现挺多博客提高这本书,大同小异都是从《精通正则表达式》的第6章总结起来的,很有意义拜读一下。
第3版电子书资源都是从网络上下载过来的
非扫描版,排版不太好,来自linux公社https://pan.baidu.com/s/14PJts0LFJA7FrzUsaQoCTw
扫描版,来自java1234.comhttps://pan.baidu.com/s/1jLSi-sgxUGCkfh_ZWwfZog

第1-3章 正则入门、示例、特性

子表达式:指的是整个正则表达式中的一部分,通常是括号内的表达式,或者是由「|」分隔的多选分支。

字符组

字符[···]里的连字符-必须放在字符组的开头,保证它是一个普通字符,而不是用来表示范围;
排除型字符组[^···] 可以匹配换行符;

\w   包括数字、字母和下划线,[a-zA-Z0-9_]
\W  等价于\[^\w]
\s  包括空格符、制表符、换行符和回车符
\S  等价于\[^\s]
\b  等价于(?<!\w)(?=\w)|(?<=\w)(?!\w)
\d  数字,[0-9]
\D  某个不是数字的字符,\[^\d]

字符组集合运算:

OR:[[abc][def]]等价于[abc[def]]、[[abc]def]、[abcdef]
AND:[[a-z]&&[^aeiou]]   小写非元音字母的字符组

Unicode属性

基本属性

\p{L}    字母
\p{M}   不能单独出现,而必须与其他基本字符一起出现(重音符号、包围框等)
\p{Z}   用于表示分割,但本身不可见的字符(各种空白字符)
\p{S}   各种图形符号和字母符号
\p{N}   任何数字字符
\p{P}   标点字符
\p{C}   匹配其他任何字符(很少用于正常字符)

复合子属性

量词

匹配优先量词:*、+、?、区间{min,max}
忽略优先量词(懒惰模式):*?、+?、??、{num,num}? 匹配尽可能少的内容
占有优先量词:*+、++、?+、{num,num}+ 一旦匹配,不会交还,可以用固化分组模拟,如.++与?>.+

锚点

环视结构

环视结构属于复杂锚点,不匹配任何字符,只匹配文本中的特定位置,这一点与单词分界符「\b」、锚点「^」和「$」相 似。但是,环视比它们更加通用。
为什么不在最终匹配的结果中包含顺序环视匹配过的文本呢?通常,这是因为我们希望在表达式的后面部分,或者在稍后应用正则表达式时,再次检测这段文本。
(?=···)肯定型顺序环视:从左至右查看文本,尝试匹配子表达式,如果能够匹配,就返回匹配成功信息

Pattern pattern = Pattern.compile("(?=Jeffrey)Jeff");Matcher matcher = pattern.matcher("by Jeffrey Friedl");//JeffMatcher matcher1 = pattern.matcher("by Jefferson Friedl");//无结果

(?<=···)肯定型逆序环视:从右向左查看文本

s/Jeffs/Jeff's/g
s/\bJeffs\b/Jeff's/g
s/\b(Jeff)(s)\b/$1'$2/g //只增加了复杂度
s/\bJeff(?=s\b)/Jeff'/g
s/(?<=\bJeff)(?=s\b)/'/g
s/(?=s\b)(?<=\bJeff)/'/g //调换环视顺序不影响结果

(?!···)否定型顺序环视:从左至右查看不匹配
(?<!···)否定型逆序环视:从右向左查看不匹配

模式修饰符

(?i)启用不区分大小写,作用范围只限于括号内部,(?-i)停用

Pattern pattern = Pattern.compile("<B>(?i)very(?-i)</B>");
Matcher matcher = pattern.matcher("<B>Very</B>");//<B>Very</B>
/g   全局替换,Java用不了,JavaScript可以
i   不区分大小写
x   宽松排列和注释模式,大多数空白字符被忽略,容许出现以#开头标记的注释,js用提示正则语法错误
m   增强的行锚点模式,^$会从字符串模式切换到逻辑行模式,即当前行开头和行结尾
s   点号通配模式/单行模式,点号可以匹配换行和各种终止符(默认点号不能匹配换行符和回车符)

模式作用范围:(?modifier:···)
添加注释:某些流派支持(?#···)和#···,测试java不支持
文字文本范围:\Q···\E,用于构建正则时包含变量,消除包含范围所有元字符的特殊含义,Java支持,但也有quote方法可以起同样的效果

分组

非捕获分组(?:···)非捕获型括号,正则引擎不需记录括号内的内容,速度快,内存少。
固化分组(?>···):一旦括号内的子表达式匹配之后,匹配的内容就固定下来。如.*!能匹配hi!,但固化分组(?>.&!)匹配不了
条件判断

(<)?\w+(?(1)>):(?(1)>)部分是指(<)?是否匹配成功,成功的话进行>的匹配
?(?<=NUM)\d+|\w+:在NUM之后的位置尝试匹配\d+,但是在其他位置匹配\w+

反向引用

用途:匹配某两个部分重复
括号能够“记忆”其中的子表达式匹配的文本,不论这些文本是什么, 元字符序列「\1」都能记住它们。再用「\1」、「\2」、「\3」等来表示第一、第二、第三 组括号匹配的文本。 所以「([a-z])([0-9])\1\2」中 的「\1」代表「[a-z]」匹配的内容,而「\2」代表「[0-9]」匹配的内容。

Pattern pattern = Pattern.compile("([a-z])([0-9])\\1\\2");
Matcher matcher = pattern.matcher("t2t2");//t2t2

示例

为金钱数字右到左每个三位加上,:(?<=\d)(?=(?:\d\d\d)+$)
用js使用了一下:

function regex() {var pattern = /(?<=\d)(?=(?:\d\d\d)+$)/g;var str = "1234567890";var str1 = str.replace(pattern, ",");document.getElementById("out").innerHTML = str1;
}
//1,234,567,890

第4章 匹配原理


用nfa|nfa not匹配nfa not测试分类,传统NFA匹配结果为nfa,DFA匹配结果为nfa not

两条普适原则

1.优先选择最左端(最靠开头)的匹配结果。
起始位置最靠左的匹配结果总是优先于其他可能的匹配结果。
这条规则的由来是:匹配先从需要查找的字符串的起始位置尝试匹配。在这里,“尝试匹配 (attempt)”的意思是,在当前位置测试整个正则表达式(可能很复杂)能匹配的每样文本。如果在当前位置测试了所有的可能之后不能找到匹配结果,就需要从字符串的第二个字符之前的位置开始重新尝试。在找到匹配结果以前必须在所有的位置重复此过程。只有在尝试过所有的起始位置(直到字符串的最后一个 字符)都不能找到匹配结果的情况下,才会报告“匹配失败”。
所以,如果要用「ORA」来匹配FLORAL,从字符串左边开始第一轮尝试会失败(因为「ORA」不能匹 配 FLO),第二轮尝试也会失败(「ORA」同样不能匹配 LOR),从第三个字符开始的尝试能够成功,所
以引擎会停下来,报告匹配结果 。

2.标准的匹配量词(「*」、「+」、「?」和「{m,n}」)是匹配优先的。
标准的匹配量词的结果可能并非所有可能中最长的,但总是尝试匹配尽可能多的字符,直至匹配上限为止。如果最终结果并非该表达式的所有可能中最长的,原因肯定是匹配字符过多导致匹配失败。

NFA引擎:表达式主导

​ 非确定型有穷自动机
​ 我们来看用「to(nite|knight|night)」匹配文本tonight的一种办法。正则表达式从「t」开始,每次检查一部分(由引擎查看表达式的一部分),同时检查“当前文本(current text)”是否匹配表达式的当前部 分。如果是,则继续表达式的下一部分,如此继续,直到表达式的所有部分都能匹配,即整个表达式能够 匹配成功。
​ 在「to(nite|knight|night)」的例子中,第一个元素是「t」,它将会重复尝试,直到在目标字符串中找 到‘t’为止。之后,就检查紧随其后的字符是否能由「o」匹配,如果能,就检查下面的元素。在本例中,“下 面的元素”指「(nite|knight|night)」它的真正含义是“「nite」或者「knight」或者「night」”。引擎会依次尝试 这 3 种可能。尝试「nite」的过程与之前一样:“尝试匹配「n」,然后是「i」,然后是「t」,最后是「e」。”如果这种尝试失败——就像本例,引擎会尝试另一种可能,如此继续下去,直到匹配成功或是报告失败。表达式中的控制权在不同的元素之间转换,所以称为“表达式主导”。
​ 实质上,在表达式主导的匹配过程中,每一个子表达式都是独立的。这不同于反向引用,子表达式之间不存在内在联系,而只是整个正则表达式的各个部分。在子表达式与正则表达式的控制结构(多选分支、括号以及匹配量词)的层级关系(layout)控制了整个匹配过程。

DFA引擎:表达式主导

​ 确定型有穷自动机
​ DFA引擎在扫描字符串时,会记录“当前有效(currently in the works)”的所有匹配可能。具体到 tonight的例子,引擎移动到 t时,它会在当前处理的匹配可能中添加一个潜在的可能:

接下来扫描的每个字符,都会更新当前的可能匹配序列。继续扫描两个字符以后的情况是:

有效的可能匹配变为两个(knight被淘汰出局)。扫描到g时,就只剩下一个可能匹配了。当h和t匹配 完成后,引擎发现匹配已经完成,报告成功。
​ 称为“文本主导”,是因为它扫描的字符串中的每个字符都对引擎进行了控制。不合适的匹配可能在扫描后继文字时会被去除。
​ 在某些情况下,“处理中的未终结匹配(partial match in progress)”可能就是一个完整的匹配。例如正 则表达式「to(…)?」,括号内的部分并不是必须出现的,但考虑到匹配优先的性质,引擎仍然会尝试匹 配括号内的部分。匹配过程中,在尝试括号内的部分时,完整匹配(‘to’)已经保留下来,以应付括号中的 内容无法匹配的情况。 如果引擎发现,文本中出现的某个字符会令所有处理中的匹配可能失效,就会返回某个之前保留的完 整匹配。如果不存在这样的完整匹配,则要报告在当前位置无法匹配。
​ NFA需要对同样的文本尝试不同的子表达式匹配,DFA目标文本中的每个字符只会检查(最多)一遍。

回溯

​ NFA 引擎最重要的性质是,它会依次处理各个子表达式或组成元素,遇到需要在两个可能成功的可能 中进行选择的时候,它会选择其一,同时记住另一个,以备稍后可能的需要。
​ 需要做出选择的情形包括量词(决定是否尝试另一次匹配)和多选结构(决定选择哪个多选分支,留 下哪个稍后尝试)。

两个要点:

哪个分支优先选择:如果需要在“进行尝试”和“跳过尝试”之间选择,对于匹配优先量词,引擎会优先选择“进行尝试”,而对于忽略优先量词,会选择“跳过尝试”。
回溯进行时保存哪个状态:距离当前最近储存的选项就是当本地失败强制回溯时返回的。

第5章 正则使用技巧

正则的平衡原则
1、只匹配期望的文本,排除不期望的文本。
2、必须易于控制和理解。
3、如果使用NFA引擎,必须保证效率(如果能够匹配,必须很快地返回匹配结果,如果不能匹配,应 该在尽可能短的时间内报告匹配失败)。

要想在复杂性和完整性之间求得平衡,一个重要的因素是了解待搜索的文本。
还有很多正则完善过程的示例。

第6章 打造高效正则

提供了Java的性能测试类,但受虚拟机执行过程中的一些因素(虚拟机类型、自动的预编译、运行时编译与代码的交互情况、对大量使用的代码反复编译和优化等内部操作),可能影响测试结果。

正则表达式的应用原理

正则表达式应用到目标字符串的过程大致分为下面几步:
1.正则表达式编译 检查正则表达式的语法正确性,如果正确,就将其编译为内部形式(internal form)。
2.传动开始 传动装置将正则引擎“定位”到目标字符串的起始位置。
3.元素检测 引擎测试正则表达式和文本,依次测试正则表达式的各个元素(comp-onent),一点补充:
相连元素,例如「Subject」中的「S」、「u」、「b」、「j」、「e」等等,会依次尝试,只有当某个元素 匹配失败时才会停止。
量词修饰的元素,控制权在量词(检查量词是否应该继续匹配)和被限定的元素(测试能否匹配) 之间轮换。
控制权在捕获型括号内外进行切换会带来一些开销。括号内的表达式匹配的文本必须保留,这样才 能通过$1来引用。因为一对括号可能属于某个回溯分支,括号的状态就是用于回溯的状态的一部分,所以进入和退出捕获型括号时需要修改状态。
4.寻找匹配结果 如果找到一个匹配结果,传统型 NFA 会“锁定”在当前状态,报告匹配成功。
5.传动装置的驱动过程 如果没有找到匹配,传动装置就会驱动引擎,从文本中的下一个字符开始新一轮的尝试(回到步骤3)。
6.匹配彻底失败 如果从目标字符串的每一个字符(包括最后一个字符之后的位置)开始的尝试都失败了,就会报告匹配彻底失败。

引擎自身优化

1、编译缓存。在第一次编译之后就把内部形式保存或缓存下来,在此后的循环中重复使用
2、预查必须字符。某些系统会在编译阶段做些额外的分析,判断是否存在成功匹配必须的字符或者字符串。在实际应用正则表达式之前,在目标字符串中快速扫描(Boyer-Moore搜索算法),检查所需的字符或者字符串——如果不存在,根本就不需要 进行任何尝试。
3、长度判断优化。 编译时会判断至少必须包含 几个字符,目标字符串的长度小于则根本不必尝试。
4、忽略优先量词。引擎必须在量词作用的对象和之后的字符之间切换,如果在捕获型括号内,控制权需在括号内外切换,会带来额外开销,针对这种情况,某些引擎有自己的优化,先预读之后的字符,迅速检测目标文本中的字符,遇到之后的字符前,取消“忽略”

优化正则表达式本身

1、避免重新编译。比如循环中应用,在循环外创建
2、优先用连接。引擎可以把「abc」当作一个元素,连接整体作为一个迭代单元,能连接的不分开,比如===比={3}快100倍,\d\d\d比\d{3}快
3、使用锚点限制匹配范围,^、$、环视、分界符

3.1  以「.*」开头的正则表达式都应该在最前面添加「^」或者「\A」,因为开头不能匹配,那么在其他位置肯定也不能匹配;
3.2 独立出锚点,比如「^(this|that)」优于「^this|^that」;
3.3 结束锚点,例如「regex(es)?$」匹配只可能从字符串末尾倒数的第8个字符开始,所以传动装置能够跳到那个位置,略过目标字符串中许多可能的字符。

4、不滥用括号和字符组,比如字符组只有单个字符是没有意义的

NFA的三种基本运算:联合运算 r|s、连接运算 rs、闭包Kleene运算 r*(形式和正则一样)
优先级:闭包运算符优先级最高,且是左结合的,连接第二,联合运算优先级最低。
依据优先级去避免冗余的括号。

5、使用非捕获型括号。节省捕获时间和回溯使用的状态数量
6、量词

用「xx*」替代「x+」能够暴露匹配必须的‘x’。同样的道理,「-{5,7}」可以写作「------{0,2}」;
排除型字符组效率比忽略优先量词高的多,比如「^[^:]*:」优于「^.*?:」;
避免.* .+,用{min,max}来规定;
避免贪婪模式,使用懒惰模式,如{0,10}?;
避免*包含正则后面的内容,不然引起不必要的回溯,如.*[0-9]*。

7、使用固化分组和占有优先量词,比如找第一个冒号,使用固化分组「(?>[:]+):」或者占有优先量词「[:]++:」,抛弃备用状态,优于「[:]+:」无意义的回溯交还
8、多选结构会引起回溯,代价很高。

避免太多多选分支;
提取必须元素,如th(is|at)优于(this|that);
字符组效率更高,因为只是简单的测试,不记录回溯状态,如[abc]优于a|b|c;
最可能匹配的分支放在前头;
结尾部分分散到多选结构内,比如a\b|b\b比(a|b)\b匹配失败更快;

示例

匹配包括转义引号的双引号字符串

"[^\\"]*(\\.[^\\"]*)*"
消除循环常用的解法:opening normal*(special normal*)*closing

以这个例子对”消除循环“进行了分析

第8章 Java

① 只有在字符组内部,\b才代表退格字符。在其他场合,\b都代表单词分界符。
②\w、\d和\s(以及对应的大写缩略法)只适用于ASCII字符,而不包括其他的字母、数字或者 Unicode 空白字符。也就是说,\d 等价于[0-9],\w 等价于[0-9a-zA-Z],\s等价于[·\t\n\f\r\x0B]。 要覆盖完整的Unicode字符,可以使用Unicode属性:用\p{L}表示\w,\p{Nd}表示\d,用 \p{Z}表示\s。(把小写的p替换为大写的P,就可以对应\W、\D和\S)。
④ 对单词分界符元字符\b和\B来说,“单词字符”的规定不同于\w和\W。单词分界符能够识别Unicode字符,而\w和\W只能识别ASCII字符。
⑤ 顺序环视结构中可以使用任意正则表达式,但是逆序环视中的子表达式只能匹配长度有限的文本。也就是说,「?」可以出现在逆序环视中,但「*」和「+」则不行。

Pattern pattern = Pattern.compile("a|b");
Matcher matcher = pattern.matcher("aef");
//修改matcher的pattren和匹配字符串
Pattern pattern1 = Pattern.compile("c|d");
matcher.usePattern(pattern1);
matcher.reset("cef");//设置和获得当前检索范围的起始和结束偏移值
matcher.region(0,3);
int start = matcher.regionStart();
int end = matcher.regionEnd();
matcher.reset();//将检索范围重新设置为整个目标字符串//检索范围是否是整个目标字符串的标志位
if(!matcher.hasAnchoringBounds()) {matcher.useAnchoringBounds(true);
}
//是否允许各种考察结构(环视、分界符)超越检索范围边界检查.默认false
if(!matcher.hasTransparentBounds()) {matcher.useTransparentBounds(true);
}//获取当前pattern的捕获型括号的数目
int count = matcher.groupCount();
//获取对应的MatchResult对象
MatchResult res = matcher.toMatchResult();
//find如果多次调用,则每次都在上次的匹配位置之后尝试新的匹配
//如果指定了整型参数,匹配尝试会从距离目标字符串开头offset个字符的位置开始。
while (matcher.find()){System.out.println(matcher.group());
}//还有一些高阶查找替换
String new1 = matcher.replaceAll("new");
String new2 = matcher.replaceFirst("new");
StringBuffer new3 = new StringBuffer();
matcher.usePattern(Pattern.compile("\\w+"));
matcher.reset("-->one+test<--");
while (matcher.find()){matcher.appendReplacement(new3, "XXX");
}
//第一轮,new3="-->XXX"
//第二轮,new3="-->XXX+XXX"
matcher.appendTail(new3);//把剩余的拼接上//Pattern的split
String[] result = Pattern.compile("\\.").split("209.204.146.22");
//limit<0,不设limit,默认为0保留数组结尾的空元素
String[] result1 = Pattern.compile(":").split(":XX:",-1);
//limit>0,正则最多应用limit-1次,返回最多包括limit个元素,最后一个元素是前面匹配剩下的

尽信书不如无书,自己对正则理解得也不是很深,还在学习,有大佬路过瞟到了错误或不准确的地方,欢迎留下指导之言!!

【读书笔记】精通正则表达式相关推荐

  1. 区块链/以太坊/读书笔记/精通以太坊思维导图

    第一章-第三章 学习笔记 思维导图 附:文本结构 精通以太坊-基础1-3章概念基于区块链技术打造的平台智能合约以太坊计算基础框架上执行的程序DApp狭义基于智能合约开发的用户界面至少包含一个智能合约广 ...

  2. 《精通正则表达式》笔记 --- 选择引号内的文字

    这个例子出自<精通正则表达式>,做一下笔记帮助理解和记忆. 第一版 最简单的case就是考虑包含一对引号,那么写出来的表达式应该是这样的: ".*" 但是这个未免太简单 ...

  3. 学习Unix,可从事什么样的工作(1)《精通Unix下C语言与项目实践》读书笔记(3)...

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读 不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八的 ...

  4. 《精通Unix下C语言与项目实践》读书笔记(16)

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读  不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八 ...

  5. Java编程比C编程好吗?《精通Unix下C语言与项目实践》读书笔记(15)

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读  不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八 ...

  6. mysql数据应用从入门_MYSQL数据库应用从入门到精通----读书笔记

    mysql 1.创建数据库 create database database_name; 2.查看数据库 show database_name; 3.选择数据库 use database_name; ...

  7. 学习Unix,可从事什么样的工作(3)《精通Unix下C语言与项目实践》读书笔记(5)...

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读  不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八 ...

  8. Unix编程需要学习的内容(3)《精通Unix下C语言与项目实践》读书笔记(13)

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读  不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八 ...

  9. 学习Unix,可从事什么样的工作(2)《精通Unix下C语言与项目实践》读书笔记(4)...

    <精通Unix下C语言编程与项目实践>读书笔记(new) 文章试读 不拘一个遍程序系列:编程序不能一个脑袋钻到底,有时要学会变通,即所谓的曲线救国.一.二.三.四 职场规划:一些杂七杂八的 ...

最新文章

  1. java socket 工具_java+socket 简易聊天工具
  2. 全球及中国垃圾发电行业运营管理及十四五投资价值评估报告2021-2027年
  3. 通俗的说下浏览器的渲染过程
  4. bootstrap mysql分页_bootstrap分页
  5. 讲讲大厂面试必考的假设检验
  6. 精品软件 推荐 360 安全卫士
  7. 【LeetCode笔记】112 113. 路径总和 I II(Java、递归、DFS)
  8. python汉诺塔运行程序_用python编写一个程序,得到汉诺塔的解决方案
  9. MVVM架构~knockoutjs系列之验证信息自定义输出~续
  10. Java之数据类型的细节
  11. 你应该知道的大数据领域12大动向
  12. 计算机文本专业,15计算机专业2文字录入期中考试卷
  13. 使用强类型DataSet增加数据并获取自动增长的ID
  14. 机器人编程java面试题,7届国赛java试题 4: 机器人塔
  15. 一、宿主机Linux系统的基本环境设置
  16. 485通讯协议_终于有人把RS485通讯协议应用及缺点分析清楚了,看完收获多多
  17. vue项目中Echarts两个图表之间连接两条线
  18. pythonfor反向循环_python如何实现反向迭代
  19. 火山PC(火山视窗)超级列表框使用案例教程
  20. python搭建qt开发环境_QT开发环境搭建(Windows)

热门文章

  1. redis----缓存穿透、击穿、雪崩问题解决
  2. 两片74161实现60进制_设计60进制的计数器_用eda设计60进制计数器_74ls161(3)
  3. rock64 linux,挑战树莓派:PINE64 发布 Rock64 Media Board Computer 开发者主板
  4. Haar Wavelet Transformation
  5. Samsung Curved UHD TV 4K超高画质、4200R黄金曲面萤幕,层峰人士聪明选择 ...
  6. pdc 半圆_PDC。 我已经准备好进行革命了。 打动我。 把我吹走
  7. java gui 弹出窗口_JAVA GUI如何制作弹窗
  8. 是的,入职中软一个月(外包华为)就离职了!
  9. 鲁棒优化入门(三)——鲁棒优化工具箱RSOME快速上手与应用实例
  10. json数组字符串转list集合