Java正则表达式匹配字符串并提取中间的值

本文通过两个具体场景,深入探讨了正则表达式的实际应用,包括如何从SAML2报文中提取属性名称和值,以及如何从SQL语句中解析表名、字段名和注释。文章提供了详细的正则表达式代码示例,并解释了每部分的功能。

前言

有时候正则表达式不只是匹配一下什么数字/邮箱/身份证/日期等等,还需要匹配某一段文字,并按照既定格式提取其中的某些值.

场景一:提取SAML2报文

SAML2报文内容如下,从中提取对应的attribute namevalue.

<saml:AttributeStatement>
	<saml:Attribute Name="mail">
		<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">zhengkai.blog.csdn.net</saml:AttributeValue>
	</saml:Attribute>
	<saml:Attribute Name="cn">
		<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">amAdmin</saml:AttributeValue>
	</saml:Attribute>
</saml:AttributeStatement>

show time

public static void main(String[] args) {
		String content = "";
		content = "<saml:AttributeStatement>";
		content += "	<saml:Attribute Name=\"mail\">";
		content += "		<saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">zhengkai.blog.csdn.net</saml:AttributeValue>";
		content += "	</saml:Attribute>";
		content += "	<saml:Attribute Name=\"cn\">";
		content += "		<saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">amAdmin</saml:AttributeValue>";
		content += "	</saml:Attribute>";
		content += "</saml:AttributeStatement>";
		String pattern = "\\<saml\\:Attribute Name=\\\"(?<scope>.*?)\\\"\\>[\\s\\S]*?\\<saml\\:AttributeValue[\\s\\S]*?\\>(?<value>.*?)\\<\\/saml\\:AttributeValue\\>[\\s\\S]*?\\<\\/saml\\:Attribute\\>";
		Pattern r = Pattern.compile(pattern);
		Matcher matcher = r.matcher(saml2attributeString);
		while (matcher.find()){
			String attributeName = matcher.group("scope");
			String attributeValue = matcher.group("value");
			System.out.println("attributeName->"+attributeName +",attributeValue->"+attributeValue );
		}
	}

控制台输出

$ attributeName->mail,attributeValue->zhengkai.blog.csdn.net
$ vattributeName->cn,attributeValue->amAdmin

解析

\\<saml\\:Attribute Name=\\\"(?<scope>.*?)\\\"\\>[\\s\\S]*?\\<saml\\:AttributeValue[\\s\\S]*?\\>(?<value>.*?)\\<\\/saml\\:AttributeValue\\>[\\s\\S]*?\\<\\/saml\\:Attribute\\>
  1. (?<scope>.*?)是用于标识scope的方式,表示夹在中间的字符串都叫scope,可以通过matcher.group("scope")提取。
  2. 所有正则的符号都需要转义,所以你会看到很多\\<或者换\\"之类的\\
  3. [\\s\\S]*? 是用来匹配任意字符,表示在前后有界定的情况下(例如夹在<xxx></xxx>中间的字符串),可以忽略那些不规则字符串的匹配。

场景2:提取sql中的表名和字段

来自github网友@ydqSpringBootCodeGenerator贡献的正则表达式,非常不错,值得深刻学习.

    //匹配整个ddl,将ddl分为表名,列sql部分,表注释
    private static final Pattern DDL_PATTERN = Pattern.compile("\\s*create\\s+table\\s+(?<tableName>\\S+)[^\\(]*\\((?<columnsSQL>[\\s\\S]+)\\)[^\\)]+?(comment\\s*(=|on\\s+table)\\s*'(?<tableComment>.*?)'\\s*;?)?$", Pattern.CASE_INSENSITIVE);
    //匹配列sql部分,分别解析每一列的列名 类型 和列注释
    private static final Pattern COL_PATTERN = Pattern.compile("\\s*(?<fieldName>\\S+)\\s+(?<fieldType>\\w+)\\s*(?:\\([\\s\\d,]+\\))?((?!comment).)*(comment\\s*'(?<fieldComment>.*?)')?\\s*(,|$)", Pattern.CASE_INSENSITIVE);

    public static void parse(String sql){
        Matcher matcher = DDL_PATTERN.matcher(sql);
        if (matcher.find()){
            String tableName = matcher.group("tableName");
            String tableComment = matcher.group("tableComment");
            System.out.println(tableName + "\t\t" + tableComment);
            System.out.println("==========");
            String columnsSQL = matcher.group("columnsSQL");
            if (columnsSQL != null && columnsSQL.length() > 0){
                Matcher colMatcher = COL_PATTERN.matcher(columnsSQL);
                while (colMatcher.find()){
                    String fieldName = colMatcher.group("fieldName");
                    String fieldType = colMatcher.group("fieldType");
                    String fieldComment = colMatcher.group("fieldComment");
                    if (!"key".equalsIgnoreCase(fieldType)){
                        System.out.println(fieldName + "\t\t" + fieldType + "\t\t" + fieldComment);
                    }
                }
            }
        }
    }

    public static void main(String[] args){
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        parse("CREATE TABLE `userinfo` (\n" +
                "  `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',\n" +
                "  `username` varchar(255) NOT NULL COMMENT '用户名',\n" +
                "  `addtime` datetime NOT NULL COMMENT '创建时间',\n" +
                "  PRIMARY KEY (`user_id`)\n" +
                ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息'");
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        parse("CREATE TABLE `USER` (\n" +
                "`ID` varchar(32) PRIMARY KEY COMMENT '主键',\n" +
                "`password` varchar(32) NOT NULL COMMENT '密码',\n" +
                "`username` varchar(32) NOT NULL COMMENT '用户'\n" +
                ") ENGINE=InnoDB DEFAULT CHARSET=utf8;");
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        parse("CREATE TABLE `tb_amount` (\n" +
                "`ID` int(10) NOT NULL AUTO_INCREMENT,\n" +
                "`PRODUCT_CODE` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '产品代码',\n" +
                "`GENDER` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '性别(male,female)',\n" +
                "`MIN_INSURED_AGE` int(3) NULL DEFAULT NULL COMMENT '最小投保年龄',\n" +
                "`MAX_INSURED_AGE` int(3) NULL DEFAULT NULL COMMENT '最大投保年龄',\n" +
                "`AMOUNT` double(10, 2) NULL DEFAULT NULL COMMENT '基本保额',\n" +
                "`PREMIUM_RATE` double(10, 2) NULL DEFAULT NULL COMMENT '基本保费',\n" +
                "`YEAR_NUM` int(3) NULL DEFAULT NULL COMMENT '缴费年限',\n" +
                "`PREMIUM_TYPE` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '费率类型',\n" +
                "`INSURANCE_PERIOD` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '保险期间(30年,60年)',\n" +
                "`INSURANCE_PERIOD_TYPE` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '保险期间类型(如定期年0,定期岁1,终身2,以后终身对应值:200)',\n" +
                "`PAY_MODE` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '交费方式',\n" +
                "PRIMARY KEY (`ID`) USING BTREE\n" +
                ") ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;");
    }

控制台输出内容:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
`userinfo`		用户信息
==========
`user_id`		int		用户ID
`username`		varchar		用户名
`addtime`		datetime		创建时间
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
`USER`		null
==========
`ID`		varchar		主键
`password`		varchar		密码
`username`		varchar		用户
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
`tb_amount`		null
==========
`ID`		int		null
`PRODUCT_CODE`		varchar		产品代码
`GENDER`		varchar		性别(male,female)
`MIN_INSURED_AGE`		int		最小投保年龄
`MAX_INSURED_AGE`		int		最大投保年龄
`AMOUNT`		double		基本保额
`PREMIUM_RATE`		double		基本保费
`YEAR_NUM`		int		缴费年限
`PREMIUM_TYPE`		varchar		费率类型
`INSURANCE_PERIOD`		varchar		保险期间(30年,60年)
`INSURANCE_PERIOD_TYPE`		varchar		保险期间类型(如定期年0,定期岁1,终身2,以后终身对应值:200)
`PAY_MODE`		varchar		交费方式
<think> 首先,用户的问题是:“我想查找使用Java正则表达式匹配以特定内容开头和结尾的字符串的方法”。我需要基于供的引用和系统指令来回答。 回顾供的引用: - 引用[1]:展示了一个例子,使用正则表达式 "^D\\d*9$" 匹配以 "D" 开头,以 "9" 结尾,中间是任意个数字的字符串。 - 引用[2]:展示了匹配以 "(乙方):" 开头,以空格结尾的字符串提取中间内容。 - 引用[3]:讨论了贪婪和非贪婪匹配,使用 "\\[CQ:.*?\\]" 匹配以 "[CQ:" 开头,以 "]" 结尾的非贪婪模式。 - 引用[4]:解释了贪婪和非贪婪限定符,如使用 "?" 使匹配非贪婪。 - 引用[5]:展示了一个例子,匹配以特定字符开头(如 +, -, @, =),以数字结尾的字符串,使用 "^[+-@=](.*?)\\d$"。 用户上一次的问题是关于 Java 正则表达式匹配以特定内容开头和结尾的字符串的方法。我需要综合这些引用来给出一个清晰的回答。 系统指令: - 所有行内数学表达式使用 $...$ 格式(但不适用,因为主题是非数学)。 - 独立公式使用 $$...$$(同样不适用)。 - LaTeX 语法正确(可能需要用于正则表达式)。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾添加引用标识,如 [^1]。 回答结构: - 结构清晰,逐步解决问题。 - 引用末尾添加标识。 步骤: 1. **介绍基本方法**:解释 Java正则表达式匹配开头和结尾的基本语法。 - 使用 ^ 表示开头,$ 表示结尾。 - 例如,引用[1] 中的 "^D\\d*9$"。 2. **讨论贪婪与非贪婪匹配**:因为引用[3]和[4]到这个问题。 - 默认是贪婪匹配(尽可能长),使用 ".*?" 进行非贪婪匹配(尽可能短)。 3. **供例子**:基于引用给出几个例子。 - 匹配以某个字符串开头和结尾的完整字符串。 - 提取中间内容。 4. **代码示例**:给出一个简单的 Java 代码示例,使用 Pattern 和 Matcher 类。 - 类似于引用[2]和[5]。 5. **生成相关问题**:在回答末尾添加相关问题。 回答内容大纲: 1. **引言**:简要解释 Java 正则表达式匹配开头和结尾的方法。 2. **基本语法**: - 使用 ^ 表示开头,例如 "^start"。 - 使用 $ 表示结尾,例如 "end$"。 - 组合使用,例如 "^start.*end$"。 3. **贪婪与非贪婪**: - 解释 * vs *? 的区别。 - 引用[4]的内容。 4. **例子**: - 匹配以 "A" 开头,以 "Z" 结尾的字符串: "^A.*Z$"。 - 但需要注意贪婪问题,可能使用非贪婪,如 "^A.*?Z$"。 5. **代码实现**: - 使用 Pattern.compile 和 Matcher。 - 展示一个完整的 Java 代码片段,类似于引用[2]。 6. **结尾**:总结。 7. **相关问题**:生成 3-5 个相关问题。 保持回答真实可靠:基于供的引用。 使用中文回答。 添加引用标识: - 例如,在描述贪婪匹配时,添加 [^4]。 - 在供例子时,参考相应引用。 最终回答结构: - 开头:解释方法。 - 中间:详细步骤和例子。 - 代码块:使用 ```java ... ``` 格式。 - 相关问题:以 "§§相关问题§§" 开头,然后列表。 </think> 以下是针对您问题的详细解答:如何使用 Java 正则表达式匹配以特定内容开头和结尾的字符串。我将基于您供的站内引用逐步解释方法,确保内容真实可靠。正则表达式Java 中主要通过 `java.util.regex` 包实现,核心类包括 `Pattern` 和 `Matcher`。匹配开头和结尾的关键是使用锚点 `^`(表示字符串开头)和 `$`(表示字符串结尾),结合特定模式来定义中间内容。此外,贪婪与非贪婪匹配会影响结果精度,这在引用中已强调[^3][^4]。下面我将逐步说明方法、供示例代码,讨论常见问题。 ### 1. **基本方法和语法** 在 Java 正则表达式中: - `^` 用于匹配字符串的开头,例如 `^start` 表示字符串必须以 "start" 开头。 - `$` 用于匹配字符串的结尾,例如 `end$` 表示字符串必须以 "end" 结尾。 - 中间部分可以是任意字符序列,常用通配符如: - `.` 匹配任意单个字符(除换行符)。 - `*` 表示前一个字符出现 0 次或多次(贪婪模式,尽可能匹配更长字符串)。 - `*?` 表示非贪婪模式(尽可能匹配更短字符串),避免过度匹配[^3][^4]。 - 组合使用时,正则表达式格式为:`^开头内容.*结尾内容$` 或 `^开头内容.*?结尾内容$`(非贪婪)。特殊字符需转义,如 `\\` 表示反斜杠。 关键点: - **贪婪 vs 非贪婪**:默认 `.*` 是贪婪的,可能会匹配过多内容;使用 `.*?` 可确保精确匹配开头和结尾之间的最短段[^4]。这在处理文本如提取字符串时非常重要。 - **完整匹配**:使用 `String.matches()` 方法或 `Matcher.matches()` 检查整个字符串是否符合模式。 - **部分提取**:使用 `Matcher.find()` 和 `group()` 提取匹配的部分,尤其适用于大文本中查找多个子串[^2]。 ### 2. **示例代码** 以下是一个完整的 Java 示例,演示匹配以 "D" 开头、以 "9" 结尾的字符串中间为任意数字)。代码基于引用[1]和贪婪控制概念[^4],使用非贪婪模式确保精确性。 ```java import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexExample { public static void main(String[] args) { // 定义正则表达式:^D开头,\d*?表示0个或多个数字(非贪婪),9$结尾 String regex = "^D\\d*?9$"; // 使用非贪婪 *? 避免过度匹配 // 测试字符串数组 String[] testStrings = {"D", "D1", "D119", "D11A9", "D55555", "D6666669"}; for (String str : testStrings) { // 使用 matches() 检查整个字符串是否符合 boolean isMatch = str.matches(regex); System.out.println("\"" + str + "\" matches: " + isMatch); } // 示例:提取大文本中以特定内容开头和结尾的子串 String text = "订单: D119 产品, 客户: D6666669 反馈, 错误: D11A9"; Pattern pattern = Pattern.compile("D\\d*?9"); // 非贪婪模式,提取所有匹配项 Matcher matcher = pattern.matcher(text); System.out.println("\n提取匹配子串:"); while (matcher.find()) { System.out.println("Found: " + matcher.group()); // 输出匹配的子串 } } } ``` **输出解释**: - `"D" matches: false`(不符合,因为缺少结尾 "9")。 - `"D1" matches: false`(不符合结尾)。 - `"D119" matches: true`(符合:以 "D" 开头,中间数字,以 "9" 结尾)。 - `"D11A9" matches: false`(中间包含非数字字符)。 - `"D55555" matches: false`(不以 "9" 结尾)。 - `"D6666669" matches: true`(符合)。 - 提取子串时,输出 `Found: D119` 和 `Found: D6666669`(忽略无效匹配)。 **代码说明**: - **正则表达式解析**:`^D\\d*?9$` - `^D`: 以 "D" 开头。 - `\\d*?`: `\\d` 匹配数字,`*?` 表示非贪婪模式(匹配尽可能少的数字)。 - `9$`: 以 "9" 结尾。 - 使用 `matches()` 验证整个字符串,或 `Matcher.find()` 提取子串。 - **为什么使用非贪婪**:在复杂文本中,贪婪模式(如 `.*`)可能错误匹配过长内容;非贪婪 `.*?` 更安全[^3][^4]。 - **转义字符**:Java 中反斜杠需写为 `\\`,如 `\\d` 表示数字。 ### 3. **常见问题及解决方案** - **问题1: 贪婪匹配导致错误** 默认 `.*` 会尽可能匹配更长字符串。例如,在文本 "start middle end start end" 中,正则 `^start.*end$` 可能匹配整个字符串而非预期部分。解决方案:使用非贪婪 `^start.*?end$`,确保匹配最短有效段[^3][^4]。 - **问题2: 特殊字符处理** 如果开头或结尾内容包含正则元字符(如 `.`, `*`, `$`),需转义。例如,匹配以 "$" 开头、以 "." 结尾的字符串:正则应为 `^\\$.*?\\.$`。 - **问题3: 提取中间内容** 使用分组 `()` 提取特定部分。例子:正则 `^(开头内容)(.*?)(结尾内容)$`,然后在 `Matcher.group(2)` 获取中间内容[^2]。参考引用[2]的提取方法。 - **性能考虑**:复杂正则可能影响性能;尽量简化或预编译 `Pattern` 对象。 ### 4. **总结** Java 正则表达式匹配开头和结尾的核心是锚点 `^` 和 `$`,结合通配符定义中间内容。非贪婪模式 `.*?` 推荐用于精确控制匹配范围。实际应用中: - 验证完整字符串:用 `String.matches(regex)`。 - 提取子串:用 `Pattern.compile()` 和 `Matcher.find()`。 - 始终测试边界情况(如空字符串或特殊字符)。 如需扩展,可以结合引用[5]的字符类(如 `^[+-@=]`)定义更灵活的开头模式[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值