一看就懂的正则表达式教程
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">案例引入</a></li><li><a href="#_label1">正则表达式</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">命名规范</a></li><li><a href="#_lab2_1_1">结构组成</a></li><li><a href="#_lab2_1_2">用途场景</a></li><li><a href="#_lab2_1_3">Java中的正则校验</a></li><li><a href="#_lab2_1_4">正则元字符</a></li><ul class="third_class_ul"><li><a href="#_label3_1_4_0">正则:普通字符</a></li><li><a href="#_label3_1_4_1">正则:\d </a></li><li><a href="#_label3_1_4_2">正则:\D</a></li><li><a href="#_label3_1_4_3">正则:\w</a></li><li><a href="#_label3_1_4_4">正则:\W </a></li><li><a href="#_label3_1_4_5">正则:\s</a></li><li><a href="#_label3_1_4_6">正则:[^abc]</a></li><li><a href="#_label3_1_4_7">正则:</a></li><li><a href="#_label3_1_4_8">正则:[^a-z]</a></li><li><a href="#_label3_1_4_9">正则:\num</a></li><li><a href="#_label3_1_4_10">正则:?</a></li><li><a href="#_label3_1_4_11">正则:+</a></li><li><a href="#_label3_1_4_12">正则:{n}</a></li><li><a href="#_label3_1_4_13">正则:{n,m}</a></li><li><a href="#_label3_1_4_14">正则:*</a></li></ul></ul><li><a href="#_label2">总结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>案例引入</h2><p>在讲正则表达式前,我们不妨先从一个场景来逐渐引入。</p>
<p>你可能有过这样的经历:我们去某些网站注册帐号,当你在设置密码的时候,网站会提示你密码的长度范围,以及对应的规则限制(如下图)。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/202205261102467.jpg" /></p>
<p>根据上图,我们将密码设置规则可以描述为两个条件:</p>
<p>(1)长度为6-16位;</p>
<p>(2)密码中必须包含数字,大写字母,小写字母,特殊字符(指定字符);</p>
<p>现在假设我们不知道正则表达式,作为程序员的你,该如何去实现这样一个密码验证呢?</p>
<p>下面是我写的一个校验方法(样本):</p>
<div class="jb51code"><pre class="brush:java;">/**
* 校验用户密码是否满足设置规则
*
* @param password 用户输入的密码
* @return true-满足;false-不满足
*/
public static boolean checkPassword(String password) {
// 密码不能为空
if (password == null || password.isEmpty()) {
return false;
}
// 校验密码长度(6-16位)
int len = password.length();
if (len < 6 || len > 16) {
return false;
}
// 定义四种组合条件
boolean hasNumber = false;
boolean hasSmallLetter = false;
boolean hasBigLetter = false;
boolean hasSpecialChar = false;
// 将密码字符串拆分为单个字符,然后对每个字符进行校验
char[] chars = password.toCharArray();
for (char c : chars) {
// 是否包含数字0-9
if (c >= '0' && c <= '9') {
hasNumber = true;
continue;
}
// 是否包含小写字母a-z
if (c >= 'a' && c <= 'z') {
hasSmallLetter = true;
continue;
}
// 是否包含大写字母A-Z
if (c >= 'A' && c <= 'Z') {
hasBigLetter = true;
continue;
}
// 是否满足指定的特殊字符
if ("~@#S%*_-+=:.?".indexOf(c) > 0) {
hasSpecialChar = true;
continue;
}
// 如果某个字符不在上面四种情况,则不满足规则
return false;
}
// 如果四种组合条件均满足,则符合密码设置规则
return hasNumber && hasSmallLetter && hasBigLetter && hasSpecialChar;
}</pre></div>
<p>这个方法写得对不对呢?我们不防用几组密码去进行验证下:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/202205261102468.png" /></p>
<p>可以看到,我们列举的8组密码,都得到了验证,说明我们的方法是OK的。</p>
<p>但这样一个密码设置规则校验,我们就差不多写近 30 行的代码,是不是感觉有点累赘了呢?明明规则很简单,代码量却写了这么多,有没有什么方法可以简化我们的代码呢?当然有!于是,这时就可以让我们今天的主角正则表达式出场了。</p>
<p>下面,则是具有相同校验功能,基于正则表达式的验证方法:</p>
<div class="jb51code"><pre class="brush:java;">/**
* 通过正则表达式校验用户密码是否满足设置规则
*
* @param password 用户输入的密码
* @return true-满足;false-不满足
*/
public static boolean checkPasswordByRegex(String password) {
return Pattern.matches("^(?=.*)(?=.*)(?=.*)(?=.*[~@#S%*_\\-+=:.?]){8,20}$", password);
}</pre></div>
<p>那么它写得到底对不对呢?于是,我们可以通过上面的示例数据,继续调用该方法来进行验证:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/202205261102469.png" /></p>
<p>通过结果我们可以看到,他也是符合我们预期的。于是我们发现,在不用正则表达式的时候,我们的代码量近30行,而使用了正则表达式,代码就浓缩为了1行,也就是说,使用正则表达式时可以简化我们的代码。</p>
<p>但同时我们也可知,正则表达式具有一定的学习成本,如果你不懂正则表达式,那么你看它可能就是一头雾水,如果出了问题,更也就无从下手去修改它了。</p>
<p>所以,学会正则表达式还是有必要的,至少以后你的同事写出来后,不会在脑子里出现 "这是写的啥玩意儿啊?怎么我看不懂" 的想法。</p>
<p class="maodian"><a name="_label1"></a></p><h2>正则表达式</h2>
<p>什么是正则表达式?通过上面的案例大家可能多少有点了解了。是的,他就是通过一行字符串,来描述一定的规则(如下图箭头所指红框处)。</p>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>命名规范</h3>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024610.png" /></p>
<p>正则表达式的英文为 Regular Expression,所以我们通常采用这两个单词的首几个字母合在一起,把正则表达式相关的变量名定义为 regexp(单数) 或 regexps(复数) 。</p>
<p>比如:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024611.png" /></p>
<p>又比如,在 Java 的 String 类中,有几个相关替换的方法,它也是支持正则表达式的,他的参数命名也是 regex 。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024612.png" /></p>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>结构组成</h3>
<p>正则表达式通常由一些普通字符,以及一些元字符组成。</p>
<p>普通字符:就是本身作为一个字符时,它不具有其他含义,像我们常用的大小写字母和数字。</p>
<p>元字符:就是除了本身作为一个字符外,他还可以表达其他含义(下图是部分元字符节选)。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024613.png" /></p>
<p> 其实,我们学习正则表达式,大部分就是基于元字符的学习。</p>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>用途场景</h3>
<p>学习了正则表达式,我们可以有哪些用途场景呢?</p>
<p>(1)做字符串的规则验证(比如前面的案例引入中,我们可以通过正则表达式来验证一个密码是否符合规则)。</p>
<p>(2)做字符串的替换(比如将一个字符串中所有的大小写字母去掉,或者替换为指定符号)。</p>
<p>(3)提取字符串中所需要的字符(比如一个字符串中所有的数字提取出来,组成一个新的字符串)。</p>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>Java中的正则校验</h3>
<p>正则表达式主要用途就是校验字符串,那么在Java中,只需要通过下面这个方法即可进行校验。</p>
<div class="jb51code"><pre class="brush:java;">boolean result = Pattern.matches(regex, input);</pre></div>
<p>其中:</p>
<p>regex 是我们需要写的正则表达式校验规则;</p>
<p>input 是我们待校验的字符串;</p>
<p>返回的 result 就是我们校验的结果,当为 true 的时候,表示校验通过,当为 false 的时候,则表示校验不通过。</p>
<p class="maodian"><a name="_lab2_1_4"></a></p><h3>正则元字符</h3>
<p class="maodian"><a name="_label3_1_4_0"></a></p><h4>正则:普通字符</h4>
<blockquote><p>当我们的正则表达式为一串普通字符(不包含元字符)时,校验字符串只有和正则一致时,才会校验通过。</p></blockquote>
<p>具体效果如下:</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024614.png" /></p>
<p>说明:后面例子为节省篇幅,不显得累赘,就不再贴代码,只贴校验结果。</p>
<p class="maodian"><a name="_label3_1_4_1"></a></p><h4>正则:\d </h4>
<blockquote><p>\d 表示一个数字。</p></blockquote>
<p>如:</p>
<p>aaa\d: 表示验证的字符串后面必须以 aaa 开头,且以一个数字结尾。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024715.png" /></p>
<p>aaa\dbbb:aaa和bbb中间有一个数字</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024716.png" /></p>
<p> aaa\d\d:aaa后面跟2个数字</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024717.png" /></p>
<blockquote><p><strong>注意:在Java定义的正则里,由于一个\表示的是字符串转义,因此在Java定义带有\的元字符时,还需要多写一个\,即\\,至于其他语言,自己可查阅相关资料进行了解。</strong></p></blockquote>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024718.png" /></p>
<p class="maodian"><a name="_label3_1_4_2"></a></p><h4>正则:\D</h4>
<blockquote><p>\D 表示一个非数字,它和上面 \d 的意思恰好相反。</p></blockquote>
<p>如:</p>
<p>\D\D\D: 则表示一个长度为3,不包含数字的字符串。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024719.png" /></p>
<p>111\D222:则表示111和222中间,必须包含一个非数字。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024720.png" /></p>
<p class="maodian"><a name="_label3_1_4_3"></a></p><h4>正则:\w</h4>
<blockquote><p>\w 表示一个字母(大小写均可)、数字,或下划线。</p></blockquote>
<p>如:</p>
<p>12\w45:则表示12和45中间必须是一个字母,数字,或下划线。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024721.png" /></p>
<p class="maodian"><a name="_label3_1_4_4"></a></p><h4>正则:\W </h4>
<blockquote><p>\W 与 \w 相反,表示这个位置的字符既不是字母、数字,也不是下划线。</p>
<p>也就是:特殊符号(除下划线),或者空格等满足。</p></blockquote>
<p>如:</p>
<p>12\w45:则表示12和45中间是一个非字母,非数字,或非下划线。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024722.png" /></p>
<p class="maodian"><a name="_label3_1_4_5"></a></p><h4>正则:\s</h4>
<blockquote><p>\s 表示匹配一个看不见的符号,即空格或制表符(Tab键)</p></blockquote>
<p>如:</p>
<p>88\s99:则表示88和99中间须是一个空格或制表符。</p>
<p>(由于我的编辑器设置了1个制表符替换为4个空格,所以这里就不列举制表符情况了)</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024723.png" /></p>
<p>正则:\S</p>
<blockquote><p> \S 与 \s 相反,表示一个可以看得见的符号。</p></blockquote>
<p>如:</p>
<p>88\S99:则表示88和99中间须有一个看得见的符号。 </p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024724.png" /></p>
<p>正则:.</p>
<blockquote><p> . (小数点) 则表示“\n”和"\r"之外的任何单个字符。</p></blockquote>
<p>如:</p>
<p>.... :则表示任意四个字符</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024725.png" /></p>
<p>正则:| </p>
<blockquote><p>| (竖线) 则表示或的关系,表示检测的字符串须满足其中一个时,才符合条件。</p></blockquote>
<p>如:</p>
<p>aa|bb|cc:则表示输入的字符串须是aa,或bb,或cc其中的一个。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024726.png" /></p>
<p>注意,如果我们或者关系的前后还有其它字符时,需要用()将他们包裹起来。</p>
<p>如:</p>
<p>xx(aa|bb|cc)yy:则表示输入的字符串须是xx开头,yy结尾,且中间是aa,或bb,或cc其中的一个。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024727.png" /></p>
<p>正则:</p>
<blockquote><p>[ ] 表示匹配其中任意一个字符。</p></blockquote>
<p>如: </p>
<p>ae:则表示a和e的中间须是b,或c,或d其中的一个</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024728.png" /></p>
<p>注意:用 | 表示其中之一,他可以是字符,也可以是字符串。而只用中括号时,则只表示其中一个字符。</p>
<p class="maodian"><a name="_label3_1_4_6"></a></p><h4>正则:[^abc]</h4>
<blockquote><p>[^ ] 表示不与中括号里的任意字符匹配。</p></blockquote>
<p>如: </p>
<p>a[^bcd]e:则表示a和e的中间除b,c,d这三个字符外,其他的字符都满足。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024729.png" /></p>
<p class="maodian"><a name="_label3_1_4_7"></a></p><h4>正则:</h4>
<blockquote><p>[值1-值2] 则表示值1到值2中间的所有字符都满足(包括值1和值2)。常用该正则来表示大小写字母范围,数字范围。</p></blockquote>
<p>如:</p>
<p>ae:等同于 ae,因为 b-d 其实就是b,c,d三个数。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024730.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024831.png" /></p>
<p> ae:则表示a和e中间是一个数字,等同于 a\de(前面说过\d表示一个数字)</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024832.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024833.png" /></p>
<p class="maodian"><a name="_label3_1_4_8"></a></p><h4>正则:[^a-z]</h4>
<blockquote><p>[^值1-值2] 则表示除值1和值2之外的所有字符,都可以满足。</p></blockquote>
<p>如:</p>
<p>a[^1-3]e:则表示a和e中间的字符,只要不是1,2,3,则都满足。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024834.png" /></p>
<p class="maodian"><a name="_label3_1_4_9"></a></p><h4>正则:\num</h4>
<blockquote><p>这里的num指number,也就是数字,当\后面跟数字,表示匹配第几个括号中的结果。</p></blockquote>
<p>比如:现在有 abcd 字符串,当我们用小括号把 c 包裹起来后,然后在字符串后面写上 \1,即 ab(c)d\1,则这里的 \1 就指 c,因为 \1 表示第1个小括号中的结果。</p>
<p>ab(c)d\1:等同于 abcdc 。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024835.png" /></p>
<p>如果我们继续把 ab(c)d\1 中的 d 包括起来,并在后面写上 \2,即 ab(c)(d)\1\2, 那么这里的 \2 就表示 d 这个字符,因为第2个小括号的结果是 d,所以整个表达式就等同于 abcdcd 。</p>
<p>ab(c)(d)\1\2:等同于 abcdcd,也等同于 ab(cd)\1 。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024836.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024837.png" /></p>
<p class="maodian"><a name="_label3_1_4_10"></a></p><h4>正则:?</h4>
<blockquote><p>? 表示匹配前面的子表达式零次或一次。</p></blockquote>
<p>如:</p>
<p>abc?de: 表示可匹配的字符串为 abde (匹配0次c) 或 abcde (匹配1次c)。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024838.png" /></p>
<p class="maodian"><a name="_label3_1_4_11"></a></p><h4>正则:+</h4>
<blockquote><p>匹配前面的子表达式一次或多次 (次数 >= 1,即至少1次)</p></blockquote>
<p>如:</p>
<p>abc+de:ab 和 de 之前至少有一个 c 。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024839.png" /></p>
<p class="maodian"><a name="_label3_1_4_12"></a></p><h4>正则:{<!-- -->n}</h4>
<blockquote><p>这里的 n 是一个非负整数。匹配确定的前面的子表达式 n 次。</p></blockquote>
<p>如:</p>
<p> abc{3}de:表示 ab 和 de 之间有3个c。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024840.png" /></p>
<p> ab(xx|yy){3}de:表示 ab 和 de 之间有 xx 或 yy 的个数, 一起合计为3个。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024841.png" /></p>
<p class="maodian"><a name="_label3_1_4_13"></a></p><h4>正则:{n,m}</h4>
<blockquote><p>m和n均为非负整数,其中 n<=m。最少匹配 n 次且最多匹配 m 次。</p></blockquote>
<p>如:</p>
<p>abc{2,3}de:表示 ab 和 de 之间有 2 到 3 个 c。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024842.png" /></p>
<p class="maodian"><a name="_label3_1_4_14"></a></p><h4>正则:*</h4>
<blockquote><p>表示匹配前面的子表达式任意次。</p></blockquote>
<p>如:</p>
<p>abc*de:表示 ab 和 de 之间有任意个数(包括0)c 。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202205/2022052611024843.png" /></p>
<p class="maodian"><a name="_label2"></a></p><h2>总结</h2>
頁:
[1]