网站建设改版公司四川通信建设工程有限公司网站

张小明 2025/12/31 8:49:07
网站建设改版公司,四川通信建设工程有限公司网站,网站设计 英文,网页制作知识点归纳正则表达式#xff08;Regular Expression#xff09;是一种用于描述字符串模式的强大工具#xff0c;广泛应用于字符串匹配、查找、替换、验证等场景。无论是Java开发中的数据校验、日志解析#xff0c;还是SQL中的模糊查询#xff0c;亦或是日常的文本处理#xff0c;掌…正则表达式Regular Expression是一种用于描述字符串模式的强大工具广泛应用于字符串匹配、查找、替换、验证等场景。无论是Java开发中的数据校验、日志解析还是SQL中的模糊查询亦或是日常的文本处理掌握正则表达式都能大幅提升效率。本文将从底层逻辑出发由浅入深、系统全面地讲解正则表达式结合JDK 17环境下的可运行实例帮你彻底吃透这门技术。一、为什么要学正则表达式底层价值与应用场景在讲解具体语法之前我们先搞清楚正则表达式的核心价值是什么为什么不直接用字符串的indexOf、contains等方法1. 底层价值用“模式”匹配无限字符串字符串的基础方法如contains只能匹配固定的子串而正则表达式通过定义“字符模式”可以匹配一类符合规则的字符串。例如匹配所有手机号11位数字以13/14/15/17/18/19开头匹配所有邮箱包含前为用户名后为域名匹配所有日期格式如yyyy-MM-dd、yyyy/MM/dd。这种“模式化匹配”的能力是正则表达式的核心也是其能够应对复杂字符串处理场景的根本原因。2. 核心应用场景正则表达式的应用遍布软件开发的各个环节典型场景包括数据验证用户输入的手机号、邮箱、身份证号、密码强度校验文本处理日志解析提取日志中的时间、错误码、用户ID、文本内容替换批量替换指定格式的字符串数据提取从HTML/XML文本中提取指定标签内容、从JSON字符串中提取特定字段数据库查询MySQL中的REGEXP运算符实现复杂的模糊查询配置解析解析配置文件中的特定格式配置项如.properties文件中的键值对。3. 正则表达式的执行流程底层逻辑正则表达式的执行本质上是“模式匹配引擎”对输入字符串的扫描与匹配过程。不同语言的正则引擎实现略有差异但核心流程一致编译正则表达式将正则表达式字符串转换为高效的匹配引擎有限自动机输入字符串扫描匹配引擎按顺序扫描输入字符串的每个字符模式匹配校验判断当前扫描位置的字符是否符合正则表达式定义的模式匹配结果返回若匹配成功返回匹配到的子串、位置等信息若失败返回无匹配。流程图如下二、正则表达式基础语法吃透核心元字符正则表达式的语法核心是“元字符”——具有特殊含义的字符。掌握元字符的含义和用法是学习正则表达式的基础。我们将元字符分为“基础匹配元字符”“量词元字符”“边界匹配元字符”“分组与引用元字符”四类逐一讲解。1. 基础匹配元字符匹配单个字符基础元字符用于匹配单个字符是构成正则表达式的最小单位。元字符含义示例匹配结果.匹配任意单个字符除换行符\na.baab、acb、a1b不匹配a\nb[]匹配括号内的任意一个字符[abc]a、b、c[^]匹配不在括号内的任意一个字符[^abc]d、1、不匹配a、b、c\d匹配数字字符0-9\d{3}123、456、789\D匹配非数字字符\D{2}ab、#、A1不匹配12、34\w匹配单词字符a-z、A-Z、0-9、_\whello、Hello123、user_name\W匹配非单词字符\W{2}#、$%、*\s匹配空白字符空格、制表符\t、换行符\n、回车符\r\s空格、\t、\n\r\S匹配非空白字符\S{3}abc、123、#$关键说明元字符.不匹配换行符\n若需匹配包括换行符在内的任意字符在Java中需使用Pattern.DOTALL标志方括号[]内的元字符会失去特殊含义例如[.]匹配的是字符.而非任意字符方括号[]内可以使用-表示范围例如[a-z]匹配小写字母[0-9a-zA-Z]匹配字母和数字[1-35-7]匹配1-3或5-7的数字。Java实例基础元字符匹配package com.jam.demo.regex; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 基础元字符匹配示例 * author ken */ Slf4j public class BasicMetaCharDemo { public static void main(String[] args) { // 1. 测试元字符 . 匹配任意单个字符除\n String regex1 a.b; String str1 aab\nacb\na1b\na\nb; matchAndLog(regex1, str1, 元字符 . 匹配); // 2. 测试元字符 [] 匹配括号内任意字符 String regex2 [abc]; String str2 a1b2c3d4; matchAndLog(regex2, str2, 元字符 [] 匹配); // 3. 测试元字符 [^] 匹配不在括号内的字符 String regex3 [^abc]; String str3 a1b2c3d4; matchAndLog(regex3, str3, 元字符 [^] 匹配); // 4. 测试元字符 \d 匹配数字 String regex4 \\d{3}; // 后续讲解量词这里表示匹配3个连续数字 String str4 abc123def456ghi78; matchAndLog(regex4, str4, 元字符 \\d 匹配); // 5. 测试元字符 \s 匹配空白字符 String regex5 \\s; String str5 hello world\tjava\nregex\rtest; matchAndLog(regex5, str5, 元字符 \\s 匹配); } /** * 执行正则匹配并打印结果 * param regex 正则表达式 * param input 输入字符串 * param testName 测试名称 */ private static void matchAndLog(String regex, String input, String testName) { if (StringUtils.isEmpty(regex) || StringUtils.isEmpty(input)) { log.error({}正则表达式或输入字符串不能为空, testName); return; } // 编译正则表达式推荐复用Pattern提升性能 Pattern pattern Pattern.compile(regex); Matcher matcher pattern.matcher(input); log.info( {} , testName); log.info(正则表达式{}, regex); log.info(输入字符串{}, input); log.info(匹配结果); while (matcher.find()) { // 打印匹配到的子串及其起始、结束位置 log.info(匹配到{}起始位置{}结束位置{}, matcher.group(), matcher.start(), matcher.end()); } log.info( {} 结束 \n, testName); } }运行结果关键部分 元字符 . 匹配 正则表达式a.b 输入字符串aab acb a1b a b 匹配结果 匹配到aab起始位置0结束位置3 匹配到acb起始位置4结束位置7 匹配到a1b起始位置8结束位置11 元字符 . 匹配 结束 元字符 [] 匹配 正则表达式[abc] 输入字符串a1b2c3d4 匹配结果 匹配到a起始位置0结束位置1 匹配到b起始位置2结束位置3 匹配到c起始位置4结束位置5 元字符 [] 匹配 结束 2. 量词元字符匹配多个连续字符基础元字符只能匹配单个字符量词元字符用于指定“前面的元素单个字符或分组需要匹配的次数”是实现“模式匹配”的核心。元字符含义示例匹配结果*匹配前面的元素0次或多次贪婪匹配ab*a、ab、abb、abbb匹配前面的元素1次或多次贪婪匹配abab、abb、abbb不匹配a?匹配前面的元素0次或1次贪婪匹配ab?a、ab不匹配abb{n}匹配前面的元素恰好n次ab{3}abbb恰好3个b{n,}匹配前面的元素至少n次贪婪匹配ab{2,}abb、abbb、abbbb{n,m}匹配前面的元素至少n次、至多m次贪婪ab{2,4}abb、abbb、abbbb不超过4个b*?匹配前面的元素0次或多次非贪婪匹配ab*?a、ab优先匹配最少次数?匹配前面的元素1次或多次非贪婪匹配ab?ab优先匹配最少次数??匹配前面的元素0次或1次非贪婪匹配ab??a优先匹配0次{n,m}?匹配前面的元素n到m次非贪婪ab{2,4}?abb优先匹配最少的2次关键说明贪婪匹配默认模式尽可能匹配最多的字符例如ab*匹配abbb时会匹配整个abbb而非a或ab非贪婪匹配在量词后加?尽可能匹配最少的字符例如ab*?匹配abbb时会优先匹配a而非abbb量词作用于“前面紧邻的单个元素”若需作用于多个元素需使用分组后续讲解。贪婪 vs 非贪婪核心差异实例package com.jam.demo.regex; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 贪婪匹配与非贪婪匹配对比示例 * author ken */ Slf4j public class GreedyVsLazyDemo { public static void main(String[] args) { String input aabbaaccbb; // 1. 贪婪匹配ab 尽可能匹配最多的b String greedyRegex ab; matchAndLog(greedyRegex, input, 贪婪匹配 ab); // 2. 非贪婪匹配ab? 尽可能匹配最少的b1个 String lazyRegex ab?; matchAndLog(lazyRegex, input, 非贪婪匹配 ab?); // 3. 贪婪匹配a.*b 匹配从第一个a到最后一个b的所有字符 String greedyRegex2 a.*b; matchAndLog(greedyRegex2, input, 贪婪匹配 a.*b); // 4. 非贪婪匹配a.*?b 匹配从第一个a到最近的b的字符 String lazyRegex2 a.*?b; matchAndLog(lazyRegex2, input, 非贪婪匹配 a.*?b); } /** * 执行正则匹配并打印结果复用方法 * param regex 正则表达式 * param input 输入字符串 * param testName 测试名称 */ private static void matchAndLog(String regex, String input, String testName) { if (StringUtils.isEmpty(regex) || StringUtils.isEmpty(input)) { log.error({}正则表达式或输入字符串不能为空, testName); return; } Pattern pattern Pattern.compile(regex); Matcher matcher pattern.matcher(input); log.info( {} , testName); log.info(正则表达式{}, regex); log.info(输入字符串{}, input); log.info(匹配结果); while (matcher.find()) { log.info(匹配到{}起始位置{}结束位置{}, matcher.group(), matcher.start(), matcher.end()); } log.info( {} 结束 \n, testName); } }运行结果核心差异 贪婪匹配 ab 正则表达式ab 输入字符串aabbaaccbb 匹配结果 匹配到abb起始位置1结束位置4 // 匹配到1个a后面的2个b最多 贪婪匹配 ab 结束 非贪婪匹配 ab? 正则表达式ab? 输入字符串aabbaaccbb 匹配结果 匹配到ab起始位置1结束位置3 // 匹配到1个a后面的1个b最少 非贪婪匹配 ab? 结束 贪婪匹配 a.*b 正则表达式a.*b 输入字符串aabbaaccbb 匹配结果 匹配到aabbaaccbb起始位置0结束位置10 // 从第一个a到最后一个b 贪婪匹配 a.*b 结束 非贪婪匹配 a.*?b 正则表达式a.*?b 输入字符串aabbaaccbb 匹配结果 匹配到aab起始位置0结束位置3 // 从第一个a到最近的b 非贪婪匹配 a.*?b 结束 3. 边界匹配元字符匹配字符串的边界位置边界匹配元字符不匹配具体字符而是匹配“字符串的边界位置”如字符串开头、结尾、单词边界常用于精准匹配避免部分匹配。元字符含义示例匹配结果^匹配字符串的开头多行模式下匹配行开头^abcabc字符串以abc开头不匹配xabc$匹配字符串的结尾多行模式下匹配行结尾abc$abc字符串以abc结尾不匹配abcx\b匹配单词边界单词字符与非单词字符之间\bhello\bhello单独的hello单词不匹配helloworld\B匹配非单词边界\Bhello\Bhello在单词内部如helloworld中的hello关键说明^和$在默认模式下单行模式匹配整个字符串的开头和结尾在多行模式Pattern.MULTILINE下^匹配每一行的开头$匹配每一行的结尾\b的“单词边界”是指一侧是单词字符\w另一侧是非单词字符\W或字符串边界开头/结尾。边界匹配实例精准验证场景package com.jam.demo.regex; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 边界匹配元字符示例精准验证场景 * author ken */ Slf4j public class BoundaryMatchDemo { public static void main(String[] args) { // 1. 验证字符串是否为纯数字精准匹配整个字符串都是数字 String numberRegex ^\\d$; String[] numberTests {12345, 123a45, 12345 , 0}; for (String test : numberTests) { boolean isMatch Pattern.matches(numberRegex, test); log.info(字符串{}是否为纯数字{}, test, isMatch); } // 2. 验证字符串是否以abc开头单行模式 vs 多行模式 String startWithAbc ^abc; String multiLineInput abc123\nabc456\nxabc789; // 单行模式默认只匹配整个字符串的开头 Matcher singleLineMatcher Pattern.compile(startWithAbc).matcher(multiLineInput); // 多行模式匹配每一行的开头 Matcher multiLineMatcher Pattern.compile(startWithAbc, Pattern.MULTILINE).matcher(multiLineInput); log.info(\n 单行模式下匹配 ^abc ); while (singleLineMatcher.find()) { log.info(匹配到{}位置{}~{}, singleLineMatcher.group(), singleLineMatcher.start(), singleLineMatcher.end()); } log.info(\n 多行模式下匹配 ^abc ); while (multiLineMatcher.find()) { log.info(匹配到{}位置{}~{}, multiLineMatcher.group(), multiLineMatcher.start(), multiLineMatcher.end()); } // 3. 匹配单独的hello单词不匹配helloworld或hello123 String wordRegex \\bhello\\b; String wordInput hello helloworld hello123 helloworld hello; Matcher wordMatcher Pattern.compile(wordRegex).matcher(wordInput); log.info(\n 匹配单独的hello单词 ); while (wordMatcher.find()) { log.info(匹配到{}位置{}~{}, wordMatcher.group(), wordMatcher.start(), wordMatcher.end()); } } }运行结果字符串12345是否为纯数字true 字符串123a45是否为纯数字false 字符串 12345 是否为纯数字false 字符串0是否为纯数字true 单行模式下匹配 ^abc 匹配到abc位置0~3 多行模式下匹配 ^abc 匹配到abc位置0~3 匹配到abc位置7~10 匹配单独的hello单词 匹配到hello位置0~5 匹配到hello位置24~294. 分组与引用元字符匹配多个字符的组合当需要将多个字符作为一个整体组合进行匹配时需要使用“分组”元字符。分组还支持“引用”重复匹配已匹配的分组内容和“命名分组”更清晰地获取分组结果。元字符含义示例匹配结果(pattern)捕获组将pattern作为一个分组可引用(ab)ab、abab、ababab\n引用第n个捕获组的内容n为正整数(ab)c\1abcab\1引用第一个分组的ab(?:pattern)非捕获组只分组不捕获无法引用(?:ab)ab、abab无法用\1引用(?p)命名捕获组给分组命名为name(?ab)c\kabcab\k引用命名分组ab(?pattern)正向预查匹配后面紧跟pattern的位置abc(?123)abc后面紧跟123不匹配abc456(?!pattern)负向预查匹配后面不紧跟pattern的位置abc(?!123)abc后面不紧跟123匹配abc456(?pattern)正向后查匹配前面紧跟pattern的位置(?123)abcabc前面紧跟123不匹配456abc(?!pattern)负向后查匹配前面不紧跟pattern的位置(?!123)abcabc前面不紧跟123匹配456abc关键说明捕获组会将匹配到的分组内容保存到内存中可通过Matcher.group(n)获取n从1开始0表示整个匹配结果非捕获组仅用于将多个字符视为一个整体不保存分组内容性能优于捕获组无需内存存储预查零宽断言只匹配“位置”不匹配具体字符匹配结果长度为0用于限定匹配的上下文环境命名分组在Java 7及以上支持通过Matcher.group(name)获取分组内容比数字引用更易读。分组与引用实例package com.jam.demo.regex; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 分组与引用元字符示例 * author ken */ Slf4j public class GroupReferenceDemo { public static void main(String[] args) { // 1. 捕获组匹配连续重复的abab、abab、ababab String captureGroupRegex (ab); String captureInput ab abab ababab abc; Matcher captureMatcher Pattern.compile(captureGroupRegex).matcher(captureInput); log.info( 捕获组 (ab) 匹配 ); while (captureMatcher.find()) { log.info(完整匹配{}第1个分组ab{}, captureMatcher.group(0), captureMatcher.group(1)); } // 2. 引用分组匹配ab cab第一个分组ab后面重复ab String referenceRegex (ab)c\\1; String referenceInput abcab abcxab abcabab; Matcher referenceMatcher Pattern.compile(referenceRegex).matcher(referenceInput); log.info(\n 引用分组 (ab)c\\1 匹配 ); while (referenceMatcher.find()) { log.info(完整匹配{}引用的分组内容{}, referenceMatcher.group(0), referenceMatcher.group(1)); } // 3. 命名分组匹配日期yyyy-MM-dd并提取年、月、日 String namedGroupRegex (?year\\d{4})-(?month\\d{2})-(?day\\d{2}); String dateInput 今天是2024-05-20昨天是2024-05-19明天是2024-05-21; Matcher namedMatcher Pattern.compile(namedGroupRegex).matcher(dateInput); log.info(\n 命名分组匹配日期 ); while (namedMatcher.find()) { log.info(完整日期{}年{}月{}日{}, namedMatcher.group(0), namedMatcher.group(year), namedMatcher.group(month), namedMatcher.group(day)); } // 4. 正向预查匹配后面紧跟163.com的用户名 String positiveLookaheadRegex \\w(?163\\.com); String emailInput user1163.com user2gmail.com user3163.com; Matcher positiveMatcher Pattern.compile(positiveLookaheadRegex).matcher(emailInput); log.info(\n 正向预查匹配163邮箱用户名 ); while (positiveMatcher.find()) { log.info(匹配到用户名{}, positiveMatcher.group()); } // 5. 负向后查匹配前面不是http://的URL String negativeLookbehindRegex (?!http://)\\w\\.com; String urlInput http://www.baidu.com www.google.com https://www.github.com www.163.com; Matcher negativeMatcher Pattern.compile(negativeLookbehindRegex).matcher(urlInput); log.info(\n 负向后查匹配非http开头的.com域名 ); while (negativeMatcher.find()) { log.info(匹配到域名{}, negativeMatcher.group()); } } }运行结果 捕获组 (ab) 匹配 完整匹配ab第1个分组abab 完整匹配abab第1个分组abab 完整匹配ababab第1个分组abab 引用分组 (ab)c\1 匹配 完整匹配abcab引用的分组内容ab 完整匹配abcab引用的分组内容ab 命名分组匹配日期 完整日期2024-05-20年2024月05日20 完整日期2024-05-19年2024月05日19 完整日期2024-05-21年2024月05日21 正向预查匹配163邮箱用户名 匹配到用户名user1 匹配到用户名user3 负向后查匹配非http开头的.com域名 匹配到域名www.google.com 匹配到域名www.163.com三、正则表达式进阶Java中的正则引擎与核心API掌握了正则表达式的语法后我们需要结合Java的正则API进行实际开发。Java的正则引擎基于“有限自动机”实现核心API位于java.util.regex包下主要包括Pattern正则表达式编译后的对象和Matcher匹配器对象。1. Java正则API核心类关系2. 核心API详解1Pattern类Pattern是正则表达式的编译表示线程安全可复用推荐复用避免重复编译提升性能。核心方法static Pattern compile(String regex)编译正则表达式字符串返回Pattern对象static Pattern compile(String regex, int flags)带标志的编译如Pattern.CASE_INSENSITIVE忽略大小写、Pattern.DOTALL让.匹配换行符Matcher matcher(CharSequence input)创建匹配器对象用于匹配输入字符串static boolean matches(String regex, CharSequence input)静态方法直接判断输入字符串是否匹配正则表达式等价于compile(regex).matcher(input).matches()String[] split(CharSequence input)根据正则表达式分割输入字符串返回字符串数组。2Matcher类Matcher是匹配器对象非线程安全用于执行具体的匹配操作。核心方法boolean find()查找输入字符串中是否有匹配的子串可多次调用从上次匹配结束位置继续查找boolean matches()判断整个输入字符串是否完全匹配正则表达式等价于^regex$boolean lookingAt()判断输入字符串的开头是否匹配正则表达式等价于^regexString group()返回当前匹配到的子串等价于group(0)String group(int group)返回第n个捕获组的内容n从1开始String group(String name)返回命名捕获组的内容Java 7int start()返回当前匹配子串的起始位置int end()返回当前匹配子串的结束位置 exclusiveString replaceAll(String replacement)将所有匹配的子串替换为指定字符串String replaceFirst(String replacement)将第一个匹配的子串替换为指定字符串Matcher reset()重置匹配器可重新从输入字符串开头查找。3常用标志flags标志常量含义简写Pattern.CASE_INSENSITIVE忽略大小写匹配默认只匹配ASCII字符(?i)Pattern.DOTALL让.匹配包括换行符在内的任意字符(?s)Pattern.MULTILINE多行模式^匹配行开头$匹配行结尾(?m)Pattern.UNICODE_CASE忽略大小写匹配支持Unicode字符(?u)Pattern.COMMENTS允许正则表达式中添加注释#开头到行尾(?x)标志使用实例package com.jam.demo.regex; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 正则标志使用示例 * author ken */ Slf4j public class RegexFlagsDemo { public static void main(String[] args) { // 1. CASE_INSENSITIVE忽略大小写匹配 String regex1 abc; String input1 AbC aBc ABC xyz; Pattern pattern1 Pattern.compile(regex1, Pattern.CASE_INSENSITIVE); Matcher matcher1 pattern1.matcher(input1); log.info( 忽略大小写匹配 abc ); while (matcher1.find()) { log.info(匹配到{}, matcher1.group()); } // 2. DOTALL. 匹配换行符 String regex2 a.b; String input2 a\nb a1b aab; // 无DOTALL标志不匹配换行符 Matcher matcher2 Pattern.compile(regex2).matcher(input2); // 有DOTALL标志匹配换行符 Matcher matcher2WithDotAll Pattern.compile(regex2, Pattern.DOTALL).matcher(input2); log.info(\n 无DOTALL标志匹配 a.b ); while (matcher2.find()) { log.info(匹配到{}, matcher2.group()); } log.info(\n 有DOTALL标志匹配 a.b ); while (matcher2WithDotAll.find()) { log.info(匹配到{}, matcher2WithDotAll.group()); } // 3. 简写标志在正则表达式中直接使用 (?i)(?s) 等 String regex3 (?i)abc; // 等价于 Pattern.CASE_INSENSITIVE String input3 AbC ABC aBc; Matcher matcher3 Pattern.compile(regex3).matcher(input3); log.info(\n 简写标志 (?i)abc 匹配 ); while (matcher3.find()) { log.info(匹配到{}, matcher3.group()); } } }运行结果 忽略大小写匹配 abc 匹配到AbC 匹配到aBc 匹配到ABC 无DOTALL标志匹配 a.b 匹配到a1b 匹配到aab 有DOTALL标志匹配 a.b 匹配到a b 匹配到a1b 匹配到aab 简写标志 (?i)abc 匹配 匹配到AbC 匹配到ABC 匹配到aBc3. Java正则性能优化技巧复用Pattern对象Pattern.compile()是耗时操作若正则表达式固定应将Pattern对象定义为静态常量避免重复编译优先使用非捕获组若无需引用分组内容使用(?:pattern)而非(pattern)减少内存占用避免过度使用贪婪匹配贪婪匹配可能导致回溯过多性能下降必要时使用非贪婪匹配或更精准的正则使用预查替代不必要的分组例如验证密码强度时用正向预查(?.*[A-Z])替代捕获组性能更优限制匹配范围尽量缩小正则表达式的匹配范围避免无限制的.*例如用[^]*匹配邮箱用户名而非.*。四、实战场景正则表达式在Java开发中的高频应用结合前面讲解的语法和API我们针对Java开发中的高频场景提供可直接复用的实战代码。1. 场景1用户输入验证手机号、邮箱、身份证号、密码需求手机号11位数字以13/14/15/17/18/19开头邮箱符合用户名域名格式用户名可包含字母、数字、下划线、点域名可包含多级如xxx.xxx.com身份证号18位前6位为地址码中间8位为出生日期yyyyMMdd后4位为顺序码和校验码最后一位可为X密码强度8-20位包含大小写字母、数字、特殊字符至少三种。实战代码package com.jam.demo.regex.validator; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Pattern; /** * 用户输入验证工具类正则表达式实现 * author ken */ Slf4j public class RegexValidatorUtil { /** * 手机号正则11位数字以13/14/15/17/18/19开头 */ private static final Pattern MOBILE_PHONE_PATTERN Pattern.compile(^1[345789]\\d{9}$); /** * 邮箱正则简化版覆盖大部分场景 * 用户名字母、数字、下划线、点、减号 * 域名字母、数字、下划线、点、减号至少包含一个点 */ private static final Pattern EMAIL_PATTERN Pattern.compile(^[a-zA-Z0-9_-](\\.[a-zA-Z0-9_-])*[a-zA-Z0-9_-](\\.[a-zA-Z0-9_-])*\\.[a-zA-Z]{2,}$); /** * 18位身份证号正则 */ private static final Pattern ID_CARD_18_PATTERN Pattern.compile(^[1-9]\\d{5}(19|20)\\d{2}((0[1-9])|(1[0-2]))((0[1-9])|([12]\\d)|(3[01]))\\d{3}[0-9Xx]$); /** * 密码强度正则8-20位包含大小写字母、数字、特殊字符至少三种 * 特殊字符!#$%^*()_-[]{}|;:,./? */ private static final Pattern PASSWORD_STRONG_PATTERN Pattern.compile(^(?.*[a-z])(?.*[A-Z])(?.*\\d)|(?.*[a-z])(?.*[A-Z])(?.*[!#$%^*()_-[]{}|;:\,./?])|(?.*[a-z])(?.*\\d)(?.*[!#$%^*()_-[]{}|;:\,./?])|(?.*[A-Z])(?.*\\d)(?.*[!#$%^*()_-[]{}|;:\,./?])).{8,20}$); /** * 验证手机号 * param mobile 手机号字符串 * return true验证通过false验证失败 */ public static boolean validateMobile(String mobile) { if (StringUtils.isEmpty(mobile)) { log.error(手机号验证失败输入为空); return false; } boolean isMatch MOBILE_PHONE_PATTERN.matcher(mobile).matches(); if (!isMatch) { log.error(手机号验证失败{} 格式不正确, mobile); } return isMatch; } /** * 验证邮箱 * param email 邮箱字符串 * return true验证通过false验证失败 */ public static boolean validateEmail(String email) { if (StringUtils.isEmpty(email)) { log.error(邮箱验证失败输入为空); return false; } boolean isMatch EMAIL_PATTERN.matcher(email).matches(); if (!isMatch) { log.error(邮箱验证失败{} 格式不正确, email); } return isMatch; } /** * 验证18位身份证号 * param idCard 身份证号字符串 * return true验证通过false验证失败 */ public static boolean validateIdCard18(String idCard) { if (StringUtils.isEmpty(idCard)) { log.error(身份证号验证失败输入为空); return false; } // 先验证格式 boolean isMatch ID_CARD_18_PATTERN.matcher(idCard).matches(); if (!isMatch) { log.error(身份证号验证失败{} 格式不正确, idCard); return false; } // 可选验证校验码身份证号最后一位 boolean checkCodeValid validateIdCardCheckCode(idCard); if (!checkCodeValid) { log.error(身份证号验证失败{} 校验码错误, idCard); return false; } return true; } /** * 验证密码强度 * param password 密码字符串 * return true验证通过false验证失败 */ public static boolean validatePasswordStrong(String password) { if (StringUtils.isEmpty(password)) { log.error(密码验证失败输入为空); return false; } boolean isMatch PASSWORD_STRONG_PATTERN.matcher(password).matches(); if (!isMatch) { log.error(密码验证失败{} 不符合要求8-20位包含大小写字母、数字、特殊字符至少三种, password); } return isMatch; } /** * 验证身份证号校验码18位 * 校验规则前17位数字加权求和权重为7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2 * 求和结果对11取余余数对应校验码0-1,1-0,2-X,3-9,4-8,5-7,6-6,7-5,8-4,9-3,10-2 * param idCard 18位身份证号 * return true校验码正确false校验码错误 */ private static boolean validateIdCardCheckCode(String idCard) { // 权重数组 int[] weights {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}; // 校验码对应表 char[] checkCodes {1, 0, X, 9, 8, 7, 6, 5, 4, 3, 2}; // 计算前17位加权和 int sum 0; for (int i 0; i 17; i) { sum (idCard.charAt(i) - 0) * weights[i]; } // 计算校验码 char expectedCheckCode checkCodes[sum % 11]; // 比较校验码忽略大小写 char actualCheckCode Character.toUpperCase(idCard.charAt(17)); return actualCheckCode expectedCheckCode; } // 测试方法 public static void main(String[] args) { log.info(手机号验证13812345678 → {}, validateMobile(13812345678)); log.info(手机号验证12345678901 → {}, validateMobile(12345678901)); log.info(\n邮箱验证user1163.com → {}, validateEmail(user1163.com)); log.info(邮箱验证user.com → {}, validateEmail(user.com)); log.info(\n身份证号验证110101199001011234 → {}, validateIdCard18(110101199001011234)); log.info(身份证号验证11010119900101123X → {}, validateIdCard18(11010119900101123X)); log.info(身份证号验证110101199001011235 → {}, validateIdCard18(110101199001011235)); // 校验码错误 log.info(\n密码验证Abc123!# → {}, validatePasswordStrong(Abc123!#)); log.info(密码验证abc123456 → {}, validatePasswordStrong(abc123456)); // 缺少大写和特殊字符 } }运行结果手机号验证13812345678 → true 手机号验证12345678901 → false 邮箱验证user1163.com → true 邮箱验证user.com → false 身份证号验证110101199001011234 → true 身份证号验证11010119900101123X → true 身份证号验证110101199001011235 → false 密码验证Abc123!# → true 密码验证abc123456 → false2. 场景2日志解析提取日志中的时间、错误码、用户ID需求日志格式[2024-05-20 14:30:25.123] [ERROR] [userId:1001] [errorCode:500] - 数据库查询失败提取字段时间、日志级别、用户ID、错误码、错误信息。实战代码package com.jam.demo.regex.logparse; import com.alibaba.fastjson2.JSON; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 日志解析工具类正则表达式实现 * author ken */ Slf4j public class LogParseUtil { /** * 日志正则表达式匹配格式[时间] [级别] [userId:xxx] [errorCode:xxx] - 信息 */ private static final Pattern LOG_PATTERN Pattern.compile( ^\\[(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3})\\] \\[(\\w)\\] \\[userId:(\\d)\\] \\[errorCode:(\\d)\\] - (.*)$ ); /** * 解析日志字符串 * param logStr 日志字符串 * return LogInfo 日志信息对象解析失败返回null */ public static LogInfo parseLog(String logStr) { if (StringUtils.isEmpty(logStr)) { log.error(日志解析失败输入日志为空); return null; } Matcher matcher LOG_PATTERN.matcher(logStr); if (!matcher.matches()) { log.error(日志解析失败日志格式不匹配日志内容{}, logStr); return null; } // 提取分组内容 LogInfo logInfo new LogInfo(); logInfo.setTime(matcher.group(1)); logInfo.setLevel(matcher.group(2)); logInfo.setUserId(matcher.group(3)); logInfo.setErrorCode(matcher.group(4)); logInfo.setMessage(matcher.group(5)); return logInfo; } // 日志信息封装类 Data public static class LogInfo { /** 日志时间 */ private String time; /** 日志级别INFO/ERROR/WARN/DEBUG */ private String level; /** 用户ID */ private String userId; /** 错误码 */ private String errorCode; /** 日志信息 */ private String message; } // 测试方法 public static void main(String[] args) { String log1 [2024-05-20 14:30:25.123] [ERROR] [userId:1001] [errorCode:500] - 数据库查询失败; String log2 [2024-05-20 15:40:10.456] [INFO] [userId:1002] [errorCode:0] - 用户登录成功; String log3 2024-05-20 16:50:30.789 ERROR userId:1003 errorCode:404 页面不存在; // 格式错误 LogInfo logInfo1 parseLog(log1); LogInfo logInfo2 parseLog(log2); LogInfo logInfo3 parseLog(log3); log.info(日志1解析结果{}, JSON.toJSONString(logInfo1)); log.info(日志2解析结果{}, JSON.toJSONString(logInfo2)); log.info(日志3解析结果{}, logInfo3); } }运行结果日志1解析结果{errorCode:500,level:ERROR,message:数据库查询失败,time:2024-05-20 14:30:25.123,userId:1001} 日志2解析结果{errorCode:0,level:INFO,message:用户登录成功,time:2024-05-20 15:40:10.456,userId:1002} 日志解析失败日志格式不匹配日志内容2024-05-20 16:50:30.789 ERROR userId:1003 errorCode:404 页面不存在 日志3解析结果null3. 场景3字符串替换与格式化批量替换、脱敏处理需求批量替换将字符串中的所有手机号中间4位替换为****脱敏格式标准化将字符串中的日期格式统一为yyyy-MM-dd如将2024/05/20、2024.05.20转换为2024-05-20去除空格去除字符串中的所有空白字符空格、制表符、换行符。实战代码package com.jam.demo.regex.replace; import lombok.extern.slf4j.Slf4j; import org.springframework.util.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 字符串替换与格式化工具类正则表达式实现 * author ken */ Slf4j public class StringReplaceUtil { /** * 手机号脱敏正则匹配11位手机号捕获前3位和后4位 */ private static final Pattern MOBILE_MASK_PATTERN Pattern.compile((1[345789])(\\d{4})(\\d{4})); /** * 日期格式标准化正则匹配 yyyy/MM/dd 或 yyyy.MM.dd 格式 */ private static final Pattern DATE_NORMALIZE_PATTERN Pattern.compile((\\d{4})[/.](\\d{2})[/.](\\d{2})); /** * 空白字符匹配正则匹配所有空白字符空格、制表符、换行符、回车符 */ private static final Pattern WHITESPACE_PATTERN Pattern.compile(\\s); /** * 手机号脱敏中间4位替换为**** * param input 包含手机号的字符串 * return 脱敏后的字符串 */ public static String maskMobile(String input) { if (StringUtils.isEmpty(input)) { log.warn(手机号脱敏失败输入字符串为空); return input; } // 使用分组引用替换$1表示前3位$3表示后4位 return MOBILE_MASK_PATTERN.matcher(input).replaceAll($1****$3); } /** * 日期格式标准化将 yyyy/MM/dd 或 yyyy.MM.dd 转换为 yyyy-MM-dd * param input 包含日期的字符串 * return 日期格式标准化后的字符串 */ public static String normalizeDate(String input) { if (StringUtils.isEmpty(input)) { log.warn(日期标准化失败输入字符串为空); return input; } // 分组引用替换$1年$2月$3日 return DATE_NORMALIZE_PATTERN.matcher(input).replaceAll($1-$2-$3); } /** * 去除所有空白字符空格、制表符、换行符、回车符 * param input 输入字符串 * return 去除空白后的字符串 */ public static String removeAllWhitespace(String input) { if (StringUtils.isEmpty(input)) { log.warn(去除空白失败输入字符串为空); return input; } return WHITESPACE_PATTERN.matcher(input).replaceAll(); } /** * 测试方法 * param args 命令行参数 */ public static void main(String[] args) { // 测试手机号脱敏 String mobileStr 联系电话13812345678 或 13987654321备用电话15011112222; String maskedMobile maskMobile(mobileStr); log.info(手机号脱敏前{}, mobileStr); log.info(手机号脱敏后{}, maskedMobile); // 测试日期格式标准化 String dateStr 今天是2024/05/20昨天是2024.05.19明天是2024-05-21; String normalizedDate normalizeDate(dateStr); log.info(\n日期标准化前{}, dateStr); log.info(日期标准化后{}, normalizedDate); // 测试去除所有空白字符 String whitespaceStr hello \t world \n java \r regex ; String noWhitespace removeAllWhitespace(whitespaceStr); log.info(\n去除空白前{}, whitespaceStr); log.info(去除空白后{}, noWhitespace); } }运行结果手机号脱敏前联系电话13812345678 或 13987654321备用电话15011112222 手机号脱敏后联系电话138****5678 或 139****4321备用电话150****2222 日期标准化前今天是2024/05/20昨天是2024.05.19明天是2024-05-21 日期标准化后今天是2024-05-20昨天是2024-05-19明天是2024-05-21 去除空白前 hello world java regex 去除空白后helloworldjavaregex4. 场景4MySQL中的正则表达式应用REGEXP运算符在MySQL中REGEXP或RLIKE运算符用于执行正则表达式匹配适用于复杂的模糊查询场景功能比LIKE更强大。语法SELECT column1, column2 FROM table_name WHERE column REGEXP regex_pattern;关键说明MySQL的正则表达式默认不区分大小写若需区分大小写使用REGEXP BINARY^匹配字符串开头$匹配字符串结尾.匹配任意单个字符*匹配0次或多次匹配1次或多次?匹配0次或1次[]匹配括号内的任意字符[^]匹配括号外的任意字符。实战案例MySQL 8.0环境验证准备测试表和数据-- 创建用户表 DROP TABLE IF EXISTS t_user; CREATE TABLE t_user ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL COMMENT 用户名, mobile VARCHAR(20) DEFAULT NULL COMMENT 手机号, email VARCHAR(100) DEFAULT NULL COMMENT 邮箱, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户测试表; -- 插入测试数据 INSERT INTO t_user (username, mobile, email) VALUES (zhangsan, 13812345678, zhangsan163.com), (lisi, 13987654321, lisigmail.com), (wangwu, 15011112222, wangwuqq.com), (zhaoliu, 18099998888, zhaoliucompany.com), (tianqi, 02012345678, tianqi126.com);案例1查询手机号以138或139开头的用户SELECT id, username, mobile FROM t_user WHERE mobile REGEXP ^13[89];查询结果idusernamemobile1zhangsan138123456782lisi13987654321案例2查询邮箱为163或126域名的用户SELECT id, username, email FROM t_user WHERE email REGEXP (163|126)\\.com$;查询结果idusernameemail1zhangsanzhangsan163.com5tianqitianqi126.com案例3查询用户名包含字母且长度为6的用户区分大小写SELECT id, username FROM t_user WHERE username REGEXP BINARY ^[a-zA-Z]{6}$;查询结果idusername1zhangsan2lisi3wangwu4zhaoliu案例4查询手机号不符合11位规范的用户SELECT id, username, mobile FROM t_user WHERE mobile NOT REGEXP ^1[345789]\\d{9}$;查询结果idusernamemobile5tianqi02012345678五、正则表达式避坑指南易混淆点与常见错误1. 易混淆点明确区分易混淆语法正确含义错误理解典型错误示例^和$字符串开头/结尾多行模式为行开头/结尾匹配字符^或$用^abc$匹配包含abc的字符串实际是精准匹配abc.匹配任意单个字符除换行符匹配字符.用a.b匹配a.b正确写法应为a\\.b\d和[0-9]在Java中等价匹配数字字符\d匹配任意十进制数无差异但需注意转义Java中写\\d正则原生写\d贪婪匹配 vs 非贪婪匹配贪婪尽可能多匹配非贪婪尽可能少匹配非贪婪匹配速度一定更快a.*b匹配aabbaacbb时贪婪匹配整个字符串非贪婪匹配aab捕获组 vs 非捕获组捕获组(pattern)可引用非捕获组(?:pattern)不可引用非捕获组无法分组无需引用分组时用非捕获组提升性能2. 常见错误及解决方案错误1Java中忘记转义反斜杠错误代码// 错误Java中\需要转义为\\否则编译报错 Pattern pattern Pattern.compile(\d{3});解决方案Java字符串中反斜杠需要转义正则中的\d在Java中需写为\\d。// 正确写法 Pattern pattern Pattern.compile(\\d{3});错误2用matches()方法进行部分匹配错误代码// 需求判断字符串是否包含数字错误使用matches() String input abc123def; boolean hasNumber Pattern.matches(\\d, input); // 返回false解决方案matches()方法判断整个字符串是否匹配部分匹配应使用find()方法。boolean hasNumber Pattern.compile(\\d).matcher(input).find(); // 返回true错误3正则表达式过于复杂导致回溯爆炸错误场景用(a)b匹配超长字符串aaaaaaaaaaaaaaaaaaaaaaaaaaaaa无b结尾导致程序卡顿。解决方案优化正则表达式避免嵌套量词缩小匹配范围。// 优化后避免嵌套量词 Pattern pattern Pattern.compile(ab);错误4忽略Pattern的线程安全性错误代码// 每次匹配都编译Pattern性能低下且浪费资源 public boolean isMobile(String input) { return Pattern.compile(^1[345789]\\d{9}$).matcher(input).matches(); }解决方案将Pattern定义为静态常量复用编译后的对象。private static final Pattern MOBILE_PATTERN Pattern.compile(^1[345789]\\d{9}$); public boolean isMobile(String input) { return MOBILE_PATTERN.matcher(input).matches(); }六、正则表达式性能优化与最佳实践1. 性能优化技巧1复用Pattern对象Pattern.compile()是耗时操作正则表达式固定时应将Pattern定义为静态常量避免重复编译。2优先使用非捕获组当不需要引用分组内容时使用非捕获组(?:pattern)替代捕获组(pattern)减少内存占用和匹配时间。3避免过度使用贪婪匹配贪婪匹配可能导致大量回溯必要时使用非贪婪匹配或更精准的正则表达式。例如不好的写法a.*b匹配a到最后一个b更好的写法a[^b]*b匹配a到第一个b无回溯4限制匹配范围尽量使用精准的字符集替代宽泛的匹配。例如匹配邮箱用户名用[a-zA-Z0-9_-]替代.*匹配日期用\\d{4}-\\d{2}-\\d{2}替代.*5使用预查替代不必要的分组预查零宽断言只匹配位置不消耗字符性能优于分组。例如验证密码强度// 正向预查密码包含大写字母、小写字母、数字 String regex ^(?.*[a-z])(?.*[A-Z])(?.*\\d).{8,20}$;2. 最佳实践1明确正则表达式的适用场景正则表达式适合处理结构化字符串如手机号、邮箱、日期不适合处理非结构化字符串如HTML/XML解析推荐使用专门的解析库。2编写可维护的正则表达式复杂正则表达式添加注释使用Pattern.COMMENTS标志注释以#开头拆分复杂正则为多个简单正则分步匹配。3测试正则表达式的边界情况针对以下边界情况进行测试空字符串最大长度字符串临界值如手机号10位、12位身份证号17位、19位。4使用工具辅助编写正则表达式推荐工具Regex101在线正则表达式测试工具支持Java、Python等多种语言RegexBuddy桌面端正则表达式编辑和测试工具适合复杂正则编写。七、总结正则表达式是处理字符串的“瑞士军刀”掌握其语法和底层逻辑能大幅提升字符串处理的效率和准确性。本文从基础语法、Java核心API、实战场景、避坑指南、性能优化五个维度全面讲解了正则表达式的知识体系结合JDK 17和MySQL 8.0环境下的可运行实例帮助读者夯实基础、解决实际问题。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

劳务派遣做网站的好处wordpress模版

一张照片文本会说话的数字人!Linly-Talker实战演示 在电商直播间里,一个面容清晰、口型精准的虚拟主播正24小时不间断地讲解商品;在在线课堂上,一位教师的数字分身正在用温和语调复述知识点;而在企业客服页面&#xff…

张小明 2025/12/25 22:39:04 网站建设

国外网站会让国内人做吗建设银行网站网页丢失

MacType高DPI终极方案:完美解决Windows高分屏字体模糊问题 【免费下载链接】mactype Better font rendering for Windows. 项目地址: https://gitcode.com/gh_mirrors/ma/mactype 在4K显示器上阅读文档时,你是否经常感到眼睛疲劳?Wind…

张小明 2025/12/25 22:39:06 网站建设

宜春建设网站新零售电商平台

情感语音合成的边界与责任:当AI学会“动情” 在某次开源社区的技术分享会上,一位开发者展示了用一段3秒的家庭录音,让AI模仿亲人的声音读出一封未曾写完的信。语音播放的瞬间,全场安静。那熟悉的语调、微微上扬的尾音,…

张小明 2025/12/25 22:39:03 网站建设

网站集约化建设纪要义乌网络推广公司

元宇宙的崛起与测试新需求 元宇宙作为融合虚拟现实(VR)、增强现实(AR)、区块链和人工智能(AI)的沉浸式数字空间,正迅速从概念走向现实。它不仅是娱乐和社交的延伸,更涉及教育、医疗…

张小明 2025/12/25 22:39:04 网站建设

苏州企业建站程序做分销网站好吗

封装的好处: 1、为了更加方便调用,一些固定参数不用多次写入。 2、同时如果底层代码修改,例如:传入参数如果有变动,你有100处位置调用了此方法,如果不用封装,需要修改100次。用了封装&#xff0…

张小明 2025/12/25 22:39:05 网站建设

中国做视频网站有哪些内容seo论坛

Noi浏览器豆包AI集成指南:一站式智能助手解决方案 【免费下载链接】Noi 项目地址: https://gitcode.com/GitHub_Trending/no/Noi 还在为频繁切换AI平台而烦恼吗?Noi浏览器通过创新的扩展机制,将字节跳动豆包AI无缝整合到你的工作流程…

张小明 2025/12/29 12:56:20 网站建设