服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Java字符串 正则表达式详解

Java字符串 正则表达式详解

2021-12-23 13:01半城抹茶 Java教程

这篇文章主要介绍了java使用正则表达式查找包含的字符串功能,结合具体实例形式分析了java针对字符串匹配查找的相关实现技巧,需要的朋友可以参考下

在日常Java后端开发过程中,免不了对数据字段的解析,自然就少不了对字符串的操作,这其中就包含了正则表达式这一块的内容,这里面涉及Java包中Pattern类和Macher类,本篇博客就针对这一块内容和常见的用法进行总结,本博客主要的参考资料是《Java编程思想》第4版。

以一个问题引出本博客的内容。问题是:检查一个字符串是否以大写字母开头,以句号结尾

?
1
2
3
4
String len="^[A-Z].*[\\.]$";
System.out.println("Taaa.".matches(len));//true
System.out.println("taaa.".matches(len));//false
System.out.println("Taaa".matches(len));//false

1.*[\.] 就 是 正 则 表 达 式 , 用 来 匹 配 字 符 串 。 代 表 一 行 的 起 始 , [ A − Z ] 表 是 A 到 Z 任 何 的 字 母 , 就是正则表达式,用来匹配字符串。^代表一行的起始,[A-Z]表是A到Z任何的字母, 就是正则表达式,用来匹配字符串。代表一行的起始,[A−Z]表是A到Z任何的字母,表示的一行的结束。

一、规则表

规则表定义正则表达式该怎么写。这类东西不需要刻意去记,用的时候去写就好,用多了自然就记住了。

1.字符

B 指定字符B
\xhh 十六进制为oxhh的字符
\uhhhh 十六进制表示为oxhhhh的Unicode字符
\t 制表符Tab
\n 换行符
\r 回车
\f 换页
\e 转义

2.字符类 .

. 任意字符
[abc] 包含a、b和c的任何字符(和a|b|c作用相同)
[^abc] 除了a、b和c之外的任何字符(否定)
[a-zA-Z] 从a到z或从A到Z的任何字符(范围)
[abc[hij]] 任意a、b、c、h、i和j字符(与a|b|c|h|i|j作用相同)
[a-z&&[hij]] 任意h、i或j
\s 空白符(空格、tab、换行、换页和回车)
\S 非空白符
\d 数字
\D 非数字
\w 词字符[a-zA-Z0-9]
\W 非词字符

3.边界匹配符

^ 一行的起始
$ 一行的结束
\b 词的边界
\B 非词的边界
\G 前一个匹配的结束

4.逻辑操作符

XY Y跟在X后面
X|Y X或Y
(X) 捕获组

5.量词

贪婪型 勉强型 占有型 如何匹配
X? X?? X?+ 一个或零个X
X* X*? X*+ 零个或多个X
X+ X+? X++ 一个或多个X
X{n} X{n}? X{n}+ 恰好n次X
X{n,} X{n,}? X{n,}+ 至少n次X
X{n,m} X{n,m}? X{n,m}+ X至少n次,且不超过m次

二、Pattern类

1.Pattern类的实例获取—compile方法

1.下面主要是Pattern类源码下一些重要的方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private Pattern(String p, int f) {
    pattern = p;
    flags = f;
 
    // to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present
    if ((flags & UNICODE_CHARACTER_CLASS) != 0)
        flags |= UNICODE_CASE;
 
    // Reset group index count
    capturingGroupCount = 1;
    localCount = 0;
 
    if (!pattern.isEmpty()) {
        try {
            compile();
        } catch (StackOverflowError soe) {
            throw error("Stack overflow during pattern compilation");
        }
    } else {
        root = new Start(lastAccept);
        matchRoot = lastAccept;
    }
}

从Pattern类代码可以看出构造器是私有的,故无法使用new去获得Pattern类对象实例。

仔细查询Pattern类下代码后发现通过静态方法compile去获取Pattern类的实例对象。

?
1
2
3
public static Pattern compile(String regex) {
    return new Pattern(regex, 0);
}

通过compile方法生成一个Pattern类实例对象,然后将想要匹配的字段传入Pattern对象的matcher()方法,matcher()方法会返回生成一个Matcher类实例对象,这个对象也有很多对应的方法。所以Matcher类也是无法通过new去创建一个实例的,而是通过matcher方法

?
1
2
3
4
5
6
7
8
9
10
public Matcher matcher(CharSequence input) {
    if (!compiled) {
        synchronized(this) {
            if (!compiled)
                compile();
        }
    }
    Matcher m = new Matcher(this, input);
    return m;
}
?
1
2
3
4
5
public static boolean matches(String regex, CharSequence input) {
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher(input);
    return m.matches();
}

所以之前博客开头那个问题,也可以用另外两种方式实现:

方式一:

?
1
2
3
4
Pattern len = Pattern.compile("^[A-Z].*[\\.]$");
Matcher matcher = len.matcher("Taaa.");
boolean matches = matcher.matches();
System.out.println(matches);

当是从源码中可以看出matches方法是类静态方法,所以没必要使用Macher类实例对象来调用方法。可以使用:

方式二:

?
1
System.out.println(Pattern.matches("^[A-Z].*[\\.]$","Taaa."));

2.split方法

下面是Split方法的源码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public String[] split(CharSequence input, int limit) {
    int index = 0;
    boolean matchLimited = limit > 0;
    ArrayList<String> matchList = new ArrayList<>();
    Matcher m = matcher(input);
 
    // Add segments before each match found
    while(m.find()) {
        if (!matchLimited || matchList.size() < limit - 1) {
            if (index == 0 && index == m.start() && m.start() == m.end()) {
                // no empty leading substring included for zero-width match
                // at the beginning of the input char sequence.
                continue;
            }
            String match = input.subSequence(index, m.start()).toString();
            matchList.add(match);
            index = m.end();
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index,
                                             input.length()).toString();
            matchList.add(match);
            index = m.end();
        }
    }
 
    // If no match was found, return this
    if (index == 0)
        return new String[] {input.toString()};
 
    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());
 
    // Construct result
    int resultSize = matchList.size();
    if (limit == 0)
        while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
            resultSize--;
    String[] result = new String[resultSize];
    return matchList.subList(0, resultSize).toArray(result);
}

方法解析:

input:要拆分的字符序列;

limit:结果阈值;根据指定模式拆分输入序列。

limit参数作用:

limit参数控制应用模式的次数,从而影响结果数组的长度。

如果 n 大于零,那么模式至多应用 n- 1 次,数组的长度不大于 n,并且数组的最后条目将包含除最后的匹配定界符之外的所有输入。

如果 n 非正,那么将应用模式的次数不受限制,并且数组可以为任意长度。

如果 n 为零,那么应用模式的次数不受限制,数组可以为任意长度,并且将丢弃尾部空字符串。

详细讲解:假设 input=“boo:and:foo”,匹配符为"o",可知模式最多可应用4次,数组的长度最大为5;

1、当limit=-2时,应用模式的次数不受限制且数组可以为任意长度;推测模式应用4次,数组的长度为5,数组为{“b”,"",":and:f","",""};

2、当limit=2时,模式至多应用1次,数组的长度不大于 2,且第二个元素包含除最后的匹配定界符之外的所有输入;推测模式应用1次,数组的长度为2,数组为{“b”,“o:and:foo”};

3、当limit=7时,模式至多应用6次,数组的长度不大于 7;推测模式应用4次,数组的长度为5,数组为{“b”,"",":and:f","",""};

4、当limit=0时,应用模式的次数不受限制,数组可以为任意长度,并且将丢弃尾部空字符串;推测模式应用4次,数组的长度为3,数组为{“b”,"",":and:f"}。

这里m.find像迭代器,遍历输入字符串

3.Pattern中匹配标记参数

编译标记 效果
Pattern.CANON_EQ 启用规范等价。当且仅当两个字符的“正规分解(canonicaldecomposition)”都完全相同的情况下,才认定匹配。默认情况下,不考虑“规范相等性(canonical equivalence)”。
Pattern.CASE_INSENSITIVE 启用不区分大小写的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配,要想对Unicode字符进行大小不敏感的匹配,只要将UNICODE_CASE与这个标志合起来就行了。
Pattern.COMMENTS 模式中允许空白和注释。在这种模式下,匹配时会忽略(正则表达式里的)空格字符(不是指表达式里的“\s”,而是指表达式里的空格,tab,回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。
Pattern.DOTALL 启用dotall模式。在这种模式下,表达式‘.'可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式‘.'不匹配行的结束符。
Pattern.MULTILINE 启用多行模式。在这种模式下,‘^'和‘ ' 分 别 匹 配 一 行 的 开 始 和 结 束 。 此 外 , ‘ ' 仍 然 匹 配 字 符 串 的 开 始 , ‘ '分别匹配一行的开始和结束。此外,‘^'仍然匹配字符串的开始,‘ '分别匹配一行的开始和结束。此外,‘'仍然匹配字符串的开始,‘'也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。
Pattern.UNICODE_CASE 启用Unicode感知的大小写折叠。在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不敏感的匹配。默认情况下,大小写不敏感的匹配只适用于US-ASCII字符集。
Pattern.UNIX_LINES 启用Unix行模式。在这个模式下,只有‘\n'才被认作一行的中止,并且与‘.'、‘^'、以及‘$'进行匹配。

Pattern.CASE_INSENSITIVEPattern.COMMENTSPattern.MULTILINE这三个比较常用。

三、Matcher类

对照Matcher构造器源码,可知构造器将Pattern对象的引用赋于Matcher中变量parentPattern,目标字符串赋于变量text;并创建了数组groups和locals 。

数组groups是组使用的存储。存储的是当前匹配的各捕获组的first和last信息。

groups[0]存储的是组零的first,groups[1]存储的是组零的last,groups[2]存储的是组1的first,groups[3]存储的是组1的last,依次类推。

Matcher类的方法非常多,这里就不对每个方法的源码进行详细的解读了,后续如果有空会深入研究一下。将常用方法总结如下

方法名 功能作用
public int groupCount() 返回此匹配器中的捕获组数
public String group() 实际上是调用了group(int group) ,只不过参数是0
public String group(int group) 返回当前查找而获得的与组匹配的所有子串内容
public int start() 返回当前匹配的子串的第一个字符在目标字符串中的索引位置
public int start(int group) 返回当前匹配的指定组中的子串的第一个字符在目标字符串中的索引位置 。
public int end() 返回当前匹配的子串的最后一个字符的下一个位置在目标字符串中的索引位置 。
public int end(int group) 返回当前匹配的的指定组中的子串的最后一个字符的下一个位置在目标字符串中的索引位置
public boolean find() 在目标字符串里查找下一个匹配子串
public boolean find(int start) 重置此匹配器,然后尝试查找匹配该模式,从指定的位置开始查找下一个匹配的子串
public int regionStart() 报告此匹配器区域的开始索引。
public int regionEnd() 报告此匹配器区域的结束索引(不包括)。
public Matcher region(int start,int end) 设置此匹配器的区域限制。重置匹配器,然后设置区域,使其从start参数指定的索引开始,到 end参数指定的索引结束(不包括end索引处的字符)。
public boolean lookingAt() 从目标字符串开始位置进行匹配。只有在有匹配且匹配的某一子串中包含目标字符串第一个字符的情况下才会返回true。
public boolean matches() 只有完全匹配时才会返回true。
public Matcher appendReplacement(StringBuffer sb, String replacement) 将当前匹配子串替换为指定字符串,并将从上次匹配结束后到本次匹配结束后之间的字符串添加到一个StringBuffer对象中,最后返回其字符串表示形式。
public StringBuffer appendTail(StringBuffer sb) 将最后一次匹配工作后剩余的字符串添加到一个StringBuffer对象里。
public String replaceAll(String replacement) 将匹配的子串用指定的字符串替换。
public String replaceFirst(String replacement) 将匹配的第一个子串用指定的字符串替换。

 

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!

原文链接:https://blog.csdn.net/weixin_41938314/article/details/120124014

延伸 · 阅读

精彩推荐
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7482021-02-04
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06