news 2026/2/3 4:15:23

正则表达式从入门到精通:吃透底层逻辑,解决99%的实际问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
正则表达式从入门到精通:吃透底层逻辑,解决99%的实际问题

正则表达式(Regular Expression)是一种用于描述字符串模式的强大工具,广泛应用于字符串匹配、查找、替换、验证等场景。无论是Java开发中的数据校验、日志解析,还是SQL中的模糊查询,亦或是日常的文本处理,掌握正则表达式都能大幅提升效率。本文将从底层逻辑出发,由浅入深、系统全面地讲解正则表达式,结合JDK 17环境下的可运行实例,帮你彻底吃透这门技术。

一、为什么要学正则表达式?底层价值与应用场景

在讲解具体语法之前,我们先搞清楚:正则表达式的核心价值是什么?为什么不直接用字符串的indexOfcontains等方法?

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. 编译正则表达式:将正则表达式字符串转换为高效的匹配引擎(有限自动机);

  2. 输入字符串扫描:匹配引擎按顺序扫描输入字符串的每个字符;

  3. 模式匹配校验:判断当前扫描位置的字符是否符合正则表达式定义的模式;

  4. 匹配结果返回:若匹配成功,返回匹配到的子串、位置等信息;若失败,返回无匹配。

流程图如下:

二、正则表达式基础语法:吃透核心元字符

正则表达式的语法核心是“元字符”——具有特殊含义的字符。掌握元字符的含义和用法,是学习正则表达式的基础。我们将元字符分为“基础匹配元字符”“量词元字符”“边界匹配元字符”“分组与引用元字符”四类,逐一讲解。

1. 基础匹配元字符(匹配单个字符)

基础元字符用于匹配单个字符,是构成正则表达式的最小单位。

元字符含义示例匹配结果
.匹配任意单个字符(除换行符\n)a.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、_)\w+hello、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次或多次(贪婪匹配)ab+ab、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,而非aab);

  • 非贪婪匹配:在量词后加?,尽可能匹配最少的字符(例如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+? 尽可能匹配最少的b(1个) 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 hello@world @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~29

4. 分组与引用元字符(匹配多个字符的组合)

当需要将多个字符作为一个整体(组合)进行匹配时,需要使用“分组”元字符。分组还支持“引用”(重复匹配已匹配的分组内容)和“命名分组”(更清晰地获取分组结果)。

元字符含义示例匹配结果
(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. 捕获组:匹配连续重复的ab(ab、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 = "user1@163.com user2@gmail.com user3@163.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个分组(ab):'ab' 完整匹配:'abab',第1个分组(ab):'ab' 完整匹配:'ababab',第1个分组(ab):'ab' ===== 引用分组 (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详解

(1)Pattern类

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):根据正则表达式分割输入字符串,返回字符串数组。

(2)Matcher类

Matcher是匹配器对象,非线程安全,用于执行具体的匹配操作。

核心方法:

  • boolean find():查找输入字符串中是否有匹配的子串(可多次调用,从上次匹配结束位置继续查找);

  • boolean matches():判断整个输入字符串是否完全匹配正则表达式(等价于^regex$);

  • boolean lookingAt():判断输入字符串的开头是否匹配正则表达式(等价于^regex);

  • String group():返回当前匹配到的子串(等价于group(0));

  • String group(int group):返回第n个捕获组的内容(n从1开始);

  • String group(String name):返回命名捕获组的内容(Java 7+);

  • int start():返回当前匹配子串的起始位置;

  • int end():返回当前匹配子串的结束位置( exclusive);

  • String 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' 匹配到:'aBc'

3. Java正则性能优化技巧

  1. 复用Pattern对象Pattern.compile()是耗时操作,若正则表达式固定,应将Pattern对象定义为静态常量,避免重复编译;

  2. 优先使用非捕获组:若无需引用分组内容,使用(?:pattern)而非(pattern),减少内存占用;

  3. 避免过度使用贪婪匹配:贪婪匹配可能导致回溯过多,性能下降,必要时使用非贪婪匹配或更精准的正则;

  4. 使用预查替代不必要的分组:例如验证密码强度时,用正向预查(?=.*[A-Z])替代捕获组,性能更优;

  5. 限制匹配范围:尽量缩小正则表达式的匹配范围,避免无限制的.*(例如用[^@]*匹配邮箱用户名,而非.*)。

四、实战场景:正则表达式在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邮箱验证:user1@163.com → {}", validateEmail("user1@163.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 邮箱验证:user1@163.com → true 邮箱验证:user@.com → false 身份证号验证:110101199001011234 → true 身份证号验证:11010119900101123X → true 身份证号验证:110101199001011235 → false 密码验证:Abc123!@# → true 密码验证:abc123456 → false

2. 场景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解析结果:null

3. 场景3:字符串替换与格式化(批量替换、脱敏处理)

需求:
  • 批量替换:将字符串中的所有手机号中间4位替换为****(脱敏);

  • 格式标准化:将字符串中的日期格式统一为yyyy-MM-dd(如将2024/05/202024.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 去除空白后:helloworldjavaregex

4. 场景4:MySQL中的正则表达式应用(REGEXP运算符)

在MySQL中,REGEXP(或RLIKE)运算符用于执行正则表达式匹配,适用于复杂的模糊查询场景,功能比LIKE更强大。

语法:
SELECT column1, column2 FROM table_name WHERE column REGEXP 'regex_pattern';
关键说明:
  1. MySQL的正则表达式默认不区分大小写,若需区分大小写,使用REGEXP BINARY

  2. ^匹配字符串开头,$匹配字符串结尾,.匹配任意单个字符;

  3. *匹配0次或多次,+匹配1次或多次,?匹配0次或1次;

  4. []匹配括号内的任意字符,[^]匹配括号外的任意字符。

实战案例(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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户测试表'; -- 插入测试数据 INSERT INTO `t_user` (`username`, `mobile`, `email`) VALUES ('zhangsan', '13812345678', 'zhangsan@163.com'), ('lisi', '13987654321', 'lisi@gmail.com'), ('wangwu', '15011112222', 'wangwu@qq.com'), ('zhaoliu', '18099998888', 'zhaoliu@company.com'), ('tianqi', '02012345678', 'tianqi@126.com');
案例1:查询手机号以138或139开头的用户
SELECT id, username, mobile FROM t_user WHERE mobile REGEXP '^13[89]';

查询结果

idusernamemobile
1zhangsan13812345678
2lisi13987654321
案例2:查询邮箱为163或126域名的用户
SELECT id, username, email FROM t_user WHERE email REGEXP '@(163|126)\\.com$';

查询结果

idusernameemail
1zhangsanzhangsan@163.com
5tianqitianqi@126.com
案例3:查询用户名包含字母且长度为6的用户(区分大小写)
SELECT id, username FROM t_user WHERE username REGEXP BINARY '^[a-zA-Z]{6}$';

查询结果

idusername
1zhangsan
2lisi
3wangwu
4zhaoliu
案例4:查询手机号不符合11位规范的用户
SELECT id, username, mobile FROM t_user WHERE mobile NOT REGEXP '^1[345789]\\d{9}$';

查询结果

idusernamemobile
5tianqi02012345678

五、正则表达式避坑指南:易混淆点与常见错误

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. 常见错误及解决方案

错误1:Java中忘记转义反斜杠

错误代码

// 错误: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("a+b");
错误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进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/2 23:59:28

人工智能内容整理提纲

根据你提供的讲义内容&#xff0c;我将 AIE1001 Introduction to AI Programming 课程中关于人工智能&#xff08;AI&#xff09; 的所有内容整理如下&#xff0c;涵盖Week 11的LLM核心内容&#xff0c;以及相关的编程基础&#xff1a;&#x1f916; 人工智能内容整理提纲 第一…

作者头像 李华
网站建设 2026/2/2 23:56:00

QT实现点击某个菜单项切换软件主板内容

void InfraredMeasurement::slot_action_reHongWaiQt_clicked() {if (reHongWaiQt NULL){reHongWaiQt new ReHongWaiQt();}exchangedWidget(reHongWaiQt); }//切换功能列表界面 void InfraredMeasurement::exchangedWidget(QWidget* pWidget) {if (pWidget){if (currentWidge…

作者头像 李华
网站建设 2026/2/2 23:55:57

LobeChat能否用于生成问卷调查?市场调研工具包

LobeChat 能否用于生成问卷调查&#xff1f;—— 一个市场调研工具包的实践探索 在企业越来越依赖数据驱动决策的今天&#xff0c;市场调研作为洞察用户需求的核心手段&#xff0c;其效率与质量直接影响产品迭代和战略方向。然而&#xff0c;设计一份科学、严谨且具有可操作性…

作者头像 李华
网站建设 2026/2/2 23:55:56

高职510221信创系统技术应用专业产教协同育人解决方案

当前&#xff0c;信创产业作为国家数字经济安全的核心支撑&#xff0c;正加速从“政策驱动”向“政策市场”双轮驱动转型&#xff0c;全面覆盖党政、金融、能源、医疗、教育等关键行业。唯众深耕职业教育实训装备研发与产教融合服务多年&#xff0c;依托在信创领域的软硬件资源…

作者头像 李华
网站建设 2026/2/2 3:05:48

2025年12月电力行业仪器榜单:气体检测仪品牌白皮书

在工业安全与环境保护要求日益严苛的今天&#xff0c;选择一台可靠的气体检测仪&#xff0c;如同为生产线聘请一位永不倦怠的安全哨兵。许多企业都曾有过这样的经历&#xff1a;采购的设备在关键时候响应迟缓&#xff0c;数据漂移令人困扰&#xff1b;或是售后支持形同虚设&…

作者头像 李华