learn.lianglianglee.com/专栏/Java并发编程实战/04 慎重使用正则表达式.md.html
2022-05-11 18:57:05 +08:00

1175 lines
32 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>04 慎重使用正则表达式.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/Java并发编程实战/00 开篇词你为什么需要学习并发编程?.md.html">00 开篇词你为什么需要学习并发编程?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/01 如何制定性能调优标准?.md.html">01 如何制定性能调优标准?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/02 如何制定性能调优策略?.md.html">02 如何制定性能调优策略?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/03 字符串性能优化不容小觑百M内存轻松存储几十G数据.md.html">03 字符串性能优化不容小觑百M内存轻松存储几十G数据.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/Java并发编程实战/04 慎重使用正则表达式.md.html">04 慎重使用正则表达式.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/05 ArrayList还是LinkedList使用不当性能差千倍.md.html">05 ArrayList还是LinkedList使用不当性能差千倍.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/06 Stream如何提高遍历集合效率.md.html">06 Stream如何提高遍历集合效率.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/07 深入浅出HashMap的设计与优化.md.html">07 深入浅出HashMap的设计与优化.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/08 网络通信优化之IO模型如何解决高并发下IO瓶颈.md.html">08 网络通信优化之IO模型如何解决高并发下IO瓶颈.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/09 网络通信优化之序列化避免使用Java序列化.md.html">09 网络通信优化之序列化避免使用Java序列化.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/10 网络通信优化之通信协议如何优化RPC网络通信.md.html">10 网络通信优化之通信协议如何优化RPC网络通信.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/11 答疑课堂深入了解NIO的优化实现原理.md.html">11 答疑课堂深入了解NIO的优化实现原理.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/12 多线程之锁优化深入了解Synchronized同步锁的优化方法.md.html">12 多线程之锁优化深入了解Synchronized同步锁的优化方法.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/13 多线程之锁优化深入了解Lock同步锁的优化方法.md.html">13 多线程之锁优化深入了解Lock同步锁的优化方法.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/14 多线程之锁优化(下):使用乐观锁优化并行操作.md.html">14 多线程之锁优化(下):使用乐观锁优化并行操作.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/15 多线程调优(上):哪些操作导致了上下文切换?.md.html">15 多线程调优(上):哪些操作导致了上下文切换?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/16 多线程调优(下):如何优化多线程上下文切换?.md.html">16 多线程调优(下):如何优化多线程上下文切换?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/17 并发容器的使用:识别不同场景下最优容器.md.html">17 并发容器的使用:识别不同场景下最优容器.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/18 如何设置线程池大小?.md.html">18 如何设置线程池大小?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/19 如何用协程来优化多线程业务?.md.html">19 如何用协程来优化多线程业务?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/20 磨刀不误砍柴工欲知JVM调优先了解JVM内存模型.md.html">20 磨刀不误砍柴工欲知JVM调优先了解JVM内存模型.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/21 深入JVM即时编译器JIT优化Java编译.md.html">21 深入JVM即时编译器JIT优化Java编译.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/22 如何优化垃圾回收机制?.md.html">22 如何优化垃圾回收机制?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/23 如何优化JVM内存分配.md.html">23 如何优化JVM内存分配.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/24 内存持续上升,我该如何排查问题?.md.html">24 内存持续上升,我该如何排查问题?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/25 答疑课堂:模块四热点问题解答.md.html">25 答疑课堂:模块四热点问题解答.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/26 单例模式:如何创建单一对象优化系统性能?.md.html">26 单例模式:如何创建单一对象优化系统性能?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/27 原型模式与享元模式:提升系统性能的利器.md.html">27 原型模式与享元模式:提升系统性能的利器.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/28 如何使用设计模式优化并发编程?.md.html">28 如何使用设计模式优化并发编程?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/29 生产者消费者模式:电商库存设计优化.md.html">29 生产者消费者模式:电商库存设计优化.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/30 装饰器模式:如何优化电商系统中复杂的商品价格策略?.md.html">30 装饰器模式:如何优化电商系统中复杂的商品价格策略?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/31 答疑课堂:模块五思考题集锦.md.html">31 答疑课堂:模块五思考题集锦.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/32 MySQL调优之SQL语句如何写出高性能SQL语句.md.html">32 MySQL调优之SQL语句如何写出高性能SQL语句.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/33 MySQL调优之事务高并发场景下的数据库事务调优.md.html">33 MySQL调优之事务高并发场景下的数据库事务调优.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/34 MySQL调优之索引索引的失效与优化.md.html">34 MySQL调优之索引索引的失效与优化.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/35 记一次线上SQL死锁事故如何避免死锁.md.html">35 记一次线上SQL死锁事故如何避免死锁.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/36 什么时候需要分表分库?.md.html">36 什么时候需要分表分库?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/37 电商系统表设计优化案例分析.md.html">37 电商系统表设计优化案例分析.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/38 数据库参数设置优化,失之毫厘差之千里.md.html">38 数据库参数设置优化,失之毫厘差之千里.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/39 答疑课堂MySQL中InnoDB的知识点串讲.md.html">39 答疑课堂MySQL中InnoDB的知识点串讲.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/41 如何设计更优的分布式锁?.md.html">41 如何设计更优的分布式锁?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/42 电商系统的分布式事务调优.md.html">42 电商系统的分布式事务调优.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/43 如何使用缓存优化系统性能?.md.html">43 如何使用缓存优化系统性能?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/44 记一次双十一抢购性能瓶颈调优.md.html">44 记一次双十一抢购性能瓶颈调优.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/加餐 什么是数据的强、弱一致性?.md.html">加餐 什么是数据的强、弱一致性?.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/加餐 推荐几款常用的性能测试工具.md.html">加餐 推荐几款常用的性能测试工具.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/答疑课堂:模块三热点问题解答.md.html">答疑课堂:模块三热点问题解答.md.html</a>
</li>
<li>
<a href="/专栏/Java并发编程实战/结束语 栉风沐雨,砥砺前行!.md.html">结束语 栉风沐雨,砥砺前行!.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>04 慎重使用正则表达式</h1>
<pre><code>你好,我是刘超。
</code></pre>
<p>上一讲,我在讲 String 对象优化时,提到了 Split() 方法,该方法使用的正则表达式可能引起回溯问题,今天我们就来深入了解下,这究竟是怎么回事?</p>
<p>**开始之前,我们先来看一个案例,**可以帮助你更好地理解内容。</p>
<p>在一次小型项目开发中,我遇到过这样一个问题。为了宣传新品,我们开发了一个小程序,按照之前评估的访问量,这次活动预计参与用户量 30W+TPS每秒事务处理量最高 3000 左右。</p>
<p>这个结果来自我对接口做的微基准性能测试。我习惯使用 ab 工具(通过 yum -y install httpd-tools 可以快速安装)在另一台机器上对 http 请求接口进行测试。</p>
<p>我可以通过设置 -n 请求数 /-c 并发用户数来模拟线上的峰值请求,再通过 TPS、RT每秒响应时间以及每秒请求时间分布情况这三个指标来衡量接口的性能如下图所示图中隐藏部分为我的服务器地址</p>
<p><img src="assets/9c48880c13fd89bc48c0bd756a00561b.png" alt="img" /></p>
<p>就在做性能测试的时候,我发现有一个提交接口的 TPS 一直上不去,按理说这个业务非常简单,存在性能瓶颈的可能性并不大。</p>
<p>我迅速使用了排除法查找问题。首先将方法里面的业务代码全部注释,留一个空方法在这里,再看性能如何。这种方式能够很好地区分是框架性能问题,还是业务代码性能问题。</p>
<p>我快速定位到了是业务代码问题就马上逐一查看代码查找原因。我将插入数据库操作代码加上之后TPS 稍微下降了,但还是没有找到原因。最后,就只剩下 Split() 方法操作了,果然,我将 Split() 方法加入之后TPS 明显下降了。</p>
<p>可是一个 Split() 方法为什么会影响到 TPS 呢?下面我们就来了解下正则表达式的相关内容,学完了答案也就出来了。</p>
<h2>什么是正则表达式?</h2>
<p>很基础,这里带你简单回顾一下。</p>
<p>正则表达式是计算机科学的一个概念,很多语言都实现了它。正则表达式使用一些特定的元字符来检索、匹配以及替换符合规则的字符串。</p>
<p>构造正则表达式语法的元字符,由普通字符、标准字符、限定字符(量词)、定位字符(边界字符)组成。详情可见下图:</p>
<p><img src="assets/6ede246f783be477d3219f4218543691.jpg" alt="img" /></p>
<h2>正则表达式引擎</h2>
<p>正则表达式是一个用正则符号写出的公式,程序对这个公式进行语法分析,建立一个语法分析树,再根据这个分析树结合正则表达式的引擎生成执行程序(这个执行程序我们把它称作状态机,也叫状态自动机),用于字符匹配。</p>
<p>而这里的正则表达式引擎就是一套核心算法,用于建立状态机。</p>
<p>目前实现正则表达式引擎的方式有两种DFA 自动机Deterministic Final Automata 确定有限状态自动机)和 NFA 自动机Non deterministic Finite Automaton 非确定有限状态自动机)。</p>
<p>对比来看,构造 DFA 自动机的代价远大于 NFA 自动机,但 DFA 自动机的执行效率高于 NFA 自动机。</p>
<p>假设一个字符串的长度是 n如果用 DFA 自动机作为正则表达式引擎,则匹配的时间复杂度为 O(n);如果用 NFA 自动机作为正则表达式引擎,由于 NFA 自动机在匹配过程中存在大量的分支和回溯,假设 NFA 的状态数为 s则该匹配算法的时间复杂度为 Ons</p>
<p>NFA 自动机的优势是支持更多功能。例如,捕获 group、环视、占有优先量词等高级功能。这些功能都是基于子表达式独立进行匹配因此在编程语言里使用的正则表达式库都是基于 NFA 实现的。</p>
<p>那么 NFA 自动机到底是怎么进行匹配的呢?我以下面的字符和表达式来举例说明。</p>
<p>text=“aabcab”
regex=“bc”</p>
<p>NFA 自动机会读取正则表达式的每一个字符,拿去和目标字符串匹配,匹配成功就换正则表达式的下一个字符,反之就继续和目标字符串的下一个字符进行匹配。分解一下过程。</p>
<p>首先读取正则表达式的第一个匹配符和字符串的第一个字符进行比较b 对 a不匹配继续换字符串的下一个字符也是 a不匹配继续换下一个是 b匹配。</p>
<p><img src="assets/197f80286625dc814b62a1220f14c0fa.jpg" alt="img" /></p>
<p>然后同理读取正则表达式的第二个匹配符和字符串的第四个字符进行比较c 对 c匹配继续读取正则表达式的下一个字符然而后面已经没有可匹配的字符了结束。</p>
<p><img src="assets/93e48614363857393e75084b55b3e225.jpg" alt="img" /></p>
<p>这就是 NFA 自动机的匹配过程,虽然在实际应用中,碰到的正则表达式都要比这复杂,但匹配方法是一样的。</p>
<h3>NFA 自动机的回溯</h3>
<p>用 NFA 自动机实现的比较复杂的正则表达式,在匹配过程中经常会引起回溯问题。大量的回溯会长时间地占用 CPU从而带来系统性能开销。我来举例说明。</p>
<p>text=“abbc”
regex=“ab{1,3}c”</p>
<p>这个例子,匹配目的比较简单。匹配以 a 开头,以 c 结尾,中间有 1-3 个 b 字符的字符串。NFA 自动机对其解析的过程是这样的:</p>
<p>首先,读取正则表达式第一个匹配符 a 和字符串第一个字符 a 进行比较a 对 a匹配。</p>
<p><img src="assets/2cb06df017f9e2974a8bd47c081196ae.jpg" alt="img" /></p>
<p>然后,读取正则表达式第二个匹配符 b{1,3} 和字符串的第二个字符 b 进行比较,匹配。但因为 b{1,3} 表示 1-3 个 b 字符串NFA 自动机又具有贪婪特性,所以此时不会继续读取正则表达式的下一个匹配符,而是依旧使用 b{1,3} 和字符串的第三个字符 b 进行比较,结果还是匹配。</p>
<p><img src="assets/dd5c24c6cfc5a11b133bdfcfb4c43b5d.jpg" alt="img" /></p>
<p>接着继续使用 b{1,3} 和字符串的第四个字符 c 进行比较,发现不匹配了,此时就会发生回溯,已经读取的字符串第四个字符 c 将被吐出去,指针回到第三个字符 b 的位置。</p>
<p><img src="assets/9f877bcafa908991a56b0262ed2990e5.jpg" alt="img" /></p>
<p>那么发生回溯以后,匹配过程怎么继续呢?程序会读取正则表达式的下一个匹配符 c和字符串中的第四个字符 c 进行比较,结果匹配,结束。</p>
<p><img src="assets/a61f13e7540341ff064bf8d104069922.jpg" alt="img" /></p>
<h3>如何避免回溯问题?</h3>
<p>既然回溯会给系统带来性能开销,那我们如何应对呢?如果你有仔细看上面那个案例的话,你会发现 NFA 自动机的贪婪特性就是导火索,这和正则表达式的匹配模式息息相关,一起来了解一下。</p>
<p><strong>1. 贪婪模式Greedy</strong></p>
<p>顾名思义,就是在数量匹配中,如果单独使用 +、 ? 、* 或{min,max} 等量词,正则表达式会匹配尽可能多的内容。</p>
<p>例如,上边那个例子:</p>
<p>text=“abbc”
regex=“ab{1,3}c”</p>
<p>就是在贪婪模式下NFA 自动机读取了最大的匹配范围,即匹配 3 个 b 字符。匹配发生了一次失败就引起了一次回溯。如果匹配结果是“abbbc”就会匹配成功。</p>
<p>text=“abbbc”
regex=“ab{1,3}c”</p>
<p><strong>2. 懒惰模式Reluctant</strong></p>
<p>在该模式下,正则表达式会尽可能少地重复匹配字符。如果匹配成功,它会继续匹配剩余的字符串。</p>
<p>例如,在上面例子的字符后面加一个“?”,就可以开启懒惰模式。</p>
<p>text=“abc”
regex=“ab{1,3}?c”</p>
<p>匹配结果是“abc”该模式下 NFA 自动机首先选择最小的匹配范围,即匹配 1 个 b 字符,因此就避免了回溯问题。</p>
<p><strong>3. 独占模式Possessive</strong></p>
<p>同贪婪模式一样,独占模式一样会最大限度地匹配更多内容;不同的是,在独占模式下,匹配失败就会结束匹配,不会发生回溯问题。</p>
<p>还是上边的例子,在字符后面加一个“+”,就可以开启独占模式。</p>
<p>text=“abbc”
regex=“ab{1,3}+bc”</p>
<p>结果是不匹配,结束匹配,不会发生回溯问题。讲到这里,你应该非常清楚了,<strong>避免回溯的方法就是:使用懒惰模式和独占模式。</strong></p>
<p>还有开头那道“一个 split() 方法为什么会影响到 TPS”的存疑你应该也清楚了吧</p>
<p>我使用了 split() 方法提取域名并检查请求参数是否符合规定。split() 在匹配分组时遇到特殊字符产生了大量回溯,我当时是在正则表达式后加了一个需要匹配的字符和“+”,解决了这个问题。</p>
<pre><code>\\?(([A-Za-z0-9-~_=%]++\\&amp;{0,1})+)
</code></pre>
<h2>正则表达式的优化</h2>
<p>正则表达式带来的性能问题,给我敲了个警钟,在这里我也希望分享给你一些心得。任何一个细节问题,都有可能导致性能问题,而这背后折射出来的是我们对这项技术的了解不够透彻。所以我鼓励你学习性能调优,要掌握方法论,学会透过现象看本质。下面我就总结几种正则表达式的优化方法给你。</p>
<h3>1. 少用贪婪模式,多用独占模式</h3>
<p>贪婪模式会引起回溯问题,我们可以使用独占模式来避免回溯。前面详解过了,这里我就不再解释了。</p>
<h3>2. 减少分支选择</h3>
<p>分支选择类型“(X|Y|Z)”的正则表达式会降低性能,我们在开发的时候要尽量减少使用。如果一定要用,我们可以通过以下几种方式来优化:</p>
<p>首先,我们需要考虑选择的顺序,将比较常用的选择项放在前面,使它们可以较快地被匹配;</p>
<p>其次,我们可以尝试提取共用模式,例如,将“(abcd|abef)”替换为“ab(cd|ef)”,后者匹配速度较快,因为 NFA 自动机会尝试匹配 ab如果没有找到就不会再尝试任何选项</p>
<p>最后,如果是简单的分支选择类型,我们可以用三次 index 代替“(X|Y|Z)”,如果测试的话,你就会发现三次 index 的效率要比“(X|Y|Z)”高出一些。</p>
<h3>3. 减少捕获嵌套</h3>
<p>在讲这个方法之前,我先简单介绍下什么是捕获组和非捕获组。</p>
<p>捕获组是指把正则表达式中,子表达式匹配的内容保存到以数字编号或显式命名的数组中,方便后面引用。一般一个 () 就是一个捕获组,捕获组可以进行嵌套。</p>
<p>非捕获组则是指参与匹配却不进行分组编号的捕获组,其表达式一般由(?:exp组成。</p>
<p>在正则表达式中,每个捕获组都有一个编号,编号 0 代表整个匹配到的内容。我们可以看下面的例子:</p>
<pre><code>public static void main( String[] args )
{
String text = &quot;&lt;input high=\&quot;20\&quot; weight=\&quot;70\&quot;&gt;test&lt;/input&gt;&quot;;
String reg=&quot;(&lt;input.*?&gt;)(.*?)(&lt;/input&gt;)&quot;;
Pattern p = Pattern.compile(reg);
Matcher m = p.matcher(text);
while(m.find()) {
System.out.println(m.group(0));// 整个匹配到的内容
System.out.println(m.group(1));//(&lt;input.*?&gt;)
System.out.println(m.group(2));//(.*?)
System.out.println(m.group(3));//(&lt;/input&gt;)
}
}
</code></pre>
<p>运行结果:</p>
<pre><code>&lt;input high=\&quot;20\&quot; weight=\&quot;70\&quot;&gt;test&lt;/input&gt;
&lt;input high=\&quot;20\&quot; weight=\&quot;70\&quot;&gt;
test
&lt;/input&gt;
</code></pre>
<p>如果你并不需要获取某一个分组内的文本,那么就使用非捕获分组。例如,使用“(?:X)”代替“(X)”,我们再看下面的例子:</p>
<pre><code>public static void main( String[] args )
{
String text = &quot;&lt;input high=\&quot;20\&quot; weight=\&quot;70\&quot;&gt;test&lt;/input&gt;&quot;;
String reg=&quot;(?:&lt;input.*?&gt;)(.*?)(?:&lt;/input&gt;)&quot;;
Pattern p = Pattern.compile(reg);
Matcher m = p.matcher(text);
while(m.find()) {
System.out.println(m.group(0));// 整个匹配到的内容
System.out.println(m.group(1));//(.*?)
}
}
</code></pre>
<p>运行结果:</p>
<pre><code>&lt;input high=\&quot;20\&quot; weight=\&quot;70\&quot;&gt;test&lt;/input&gt;
test
</code></pre>
<p>综上可知:减少不需要获取的分组,可以提高正则表达式的性能。</p>
<h2>总结</h2>
<p>正则表达式虽然小,却有着强大的匹配功能。我们经常用到它,比如,注册页面手机号或邮箱的校验。</p>
<p>但很多时候,我们又会因为它小而忽略它的使用规则,测试用例中又没有覆盖到一些特殊用例,不乏上线就中招的情况发生。</p>
<p>综合我以往的经验来看,如果使用正则表达式能使你的代码简洁方便,那么在做好性能排查的前提下,可以去使用;如果不能,那么正则表达式能不用就不用,以此避免造成更多的性能问题。</p>
<h2>思考题</h2>
<p>除了 Split() 方法使用到正则表达式,其实 Java 还有一些方法也使用了正则表达式去实现一些功能,使我们很容易掉入陷阱。现在就请你想一想 JDK 里面,还有哪些工具方法用到了正则表达式?</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/Java并发编程实战/03 字符串性能优化不容小觑百M内存轻松存储几十G数据.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/Java并发编程实战/05 ArrayList还是LinkedList使用不当性能差千倍.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099715efe2c3d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>