修改 Pattern代码使 Java 正则表达式的group名称支持下划线 '_'

为什么

由于工作是做数据ETL的,很多时候会使用到正则对数据进行提取,但是java的正则中的groupname不支持'_',官方的文档中是这样的:

Group name

A capturing group can also be assigned a "name", a named-capturing group, and then be back-referenced later by the "name". Group names are composed of the following characters. The first character must be a letter.

The uppercase letters 'A' through 'Z' ('\u0041' through '\u005a'),
The lowercase letters 'a' through 'z' ('\u0061' through '\u007a'),
The digits '0' through '9' ('\u0030' through '\u0039'),
A named-capturing group is still numbered as described in Group number.

The captured input associated with a group is always the subsequence that the group most recently matched. If a group is evaluated a second time because of quantification then its previously-captured value, if any, will be retained if the second evaluation fails. Matching the string "aba" against the expression (a(b)?)+, for example, leaves group two set to "b". All captured input is discarded at the beginning of each match.

Groups beginning with (? are either pure, non-capturing groups that do not capture text and do not count towards the group total, or named-capturing group.

可以看到,只支持大写字母A-Z、小写字母a-z、数字0-9

查找源代码

在java.util.regex.Pattern类的以下源码中(jdk1.8.141是2789行)有下面这个方法:

    /**
     * Parses and returns the name of a "named capturing group", the trailing
     * ">" is consumed after parsing.
     */
    private String groupname(int ch) {
        StringBuilder sb = new StringBuilder();
        sb.append(Character.toChars(ch));
        while (ASCII.isLower(ch=read()) || ASCII.isUpper(ch) ||
               ASCII.isDigit(ch)) {
            sb.append(Character.toChars(ch));
        }
        if (sb.length() == 0)
            throw error("named capturing group has 0 length name");
        if (ch != '>')
            throw error("named capturing group is missing trailing '>'");
        return sb.toString();
    }

可以看到,源代码中对groupname的提取是一个while循环,当读取到的字符是小写字母(ASCII.isLower)、大写字母(ASCII.isUpper)、数字(ASCII.isDigit)的时候,会把这个字符添加到StringBuilder中,然后读取下个字符,知道不满足这个条件。

修改源代码

好,现在知道是这个原因了,怎么进行修改呢?
有很多人说不要修改大神写的代码,但是没办法。
由于不支持'_', 给工作带来挺多其它麻烦的,比如数据库中的字段名有'_',如果正则组不支持下划线的话,就需要一个正则组名和列名的映射关系,或者不用正则组名,使用正则组下标0,1,2...来映射。比较繁琐。
修改其实很简单,由于Pattern这个类在源代码中定义为final的,没法直接继承然后overwrite这个方法,就只能在自己的项目下新建一个regex包,将java.util.regex包的类都copy出来,总共是6个
1187075-20170907230140382-68873611.jpg

修改Pattern的上述方法,'_'这个字符在ASCII中是95,所以添加一个判断就可以了:

    private String groupname(int ch) {
        StringBuilder sb = new StringBuilder();
        sb.append(Character.toChars(ch));
        //TODO 增加了ch==95这个条件来支持正则组名支持下划线('_'),
        //源码为java.util.regex.Pattern的2793行
        while (ASCII.isLower(ch=read()) || ASCII.isUpper(ch) ||
               ASCII.isDigit(ch) || ch == 95) {
            sb.append(Character.toChars(ch));
        }
        if (sb.length() == 0)
            throw error("named capturing group has 0 length name");
        if (ch != '>')
            throw error("named capturing group is missing trailing '>'");
        return sb.toString();
    }

这样就可以使用我们自己Pattern类了,最后成功运行

public class MyTest {
    public static void main(String[] args) {
        Pattern pattern = Pattern.compile("\\s\\|\\s(?<my_name>worker_\\d+)\\s\\|");
        Matcher matcher = pattern.matcher("2017-02-14 23:58:04 | worker_10 | [ATMP05]");
        if (matcher.find()){
            //打印出来是"worker_10"
            System.out.println(matcher.group("my_name"));
        }
    }
}

最后,这个源码值改了一小部分,但是却让工作轻松了
当然,这样改是否会影响到其它东西需要时间的检验。

转载于:https://www.cnblogs.com/hoyas/p/7492398.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java正则表达式是一种用于匹配、查找和替换文本的强大工具。它可以用于验证输入是否符合特定的格式、从文本中提取信息、以及将文本中的某些内容替换成其他内容。 下面是一些示例代码,说明如何使用Java正则表达式进行匹配、替换和查找操作: 1. 匹配操作: ```java String input = "This is a test string."; String pattern = "test"; // 创建 Pattern 对象 Pattern p = Pattern.compile(pattern); // 执行匹配操作 Matcher m = p.matcher(input); if (m.find()) { System.out.println("Found match at index " + m.start() + " to " + m.end()); } else { System.out.println("No match found."); } ``` 这段代码首先定义了一个输入字符串和一个正则表达式模式,然后创建了一个 Pattern 对象,并使用该对象对输入字符串进行匹配操作。如果找到了匹配项,则输出其开始和结束位置;否则,输出未找到匹配项的提示信息。 2. 替换操作: ```java String input = "This is a test string."; String pattern = "test"; String replacement = "example"; // 执行替换操作 String output = input.replaceAll(pattern, replacement); System.out.println("Output string: " + output); ``` 这段代码使用了 String 类的 replaceAll() 方法,将输入字符串中所有匹配正则表达式模式的部分替换成指定的字符串。替换后的字符串将被输出到控制台。 3. 查找操作: ```java String input = "This is a test string."; String pattern = "\\w+"; // 创建 Pattern 对象 Pattern p = Pattern.compile(pattern); // 执行查找操作 Matcher m = p.matcher(input); while (m.find()) { System.out.println("Found match: " + m.group()); } ``` 这段代码使用了一个正则表达式模式,该模式匹配一个或多个字母、数字或下划线字符。然后,它创建了一个 Pattern 对象,并使用该对象对输入字符串进行查找操作。如果找到了匹配项,则输出其内容;否则,继续查找下一个匹配项。这里使用了 Matcher 类的 find() 方法来查找所有匹配项。 以上是Java正则表达式的一些基本用法,可以根据具体需求进行调整和扩展。通过学习Java正则表达式,我们可以更轻松地处理文本数据,并快速准确地完成各种文本处理任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值