Java学习日常:正则表达式

96
Happioo
2017.11.10 15:53* 字数 1418

这篇博客介绍了强大的正则表达式,和Java对它的支持。


一、正则表达式简介

在平时,我们总是通过各种ctrl + f(查找)操作来找到文本里的对应字符串。输入我们想要的字符串,然后按下确定进行查找。但很多时候,这并不能满足我们的需求,比如说我们对这个字符串有额外的限制,像是要位于行首,又比如说我们的目标是以A开头的一类字符串,而不是"一种特定的字符串"。

正则表达式可以很漂亮地解决这样的问题。通过正则表达式,我们可以将具有某些特点的字符串描述为一个表达式。比如,我们可以把"位于行首且开头为A的字符串"描述为^A.*,其中^表示行首,A表示字符A,.可以表示除了换行符以外的任何字符,而*的意思是前一个字符可以出现任意次。


二、Java中正则表达式的构建方法

就像在第一部分中看到的一样,我们通过一系列的符号构建出了我们想要的正则表达式。这些描述性的符号,可以通过点击这里来查看,非常的详细和准确,这里就不列举了。

Java中的正则表达式与其他语言的正则表达式稍有不同。在其他语言中,是一个转义符号,表示之后的字符具有特殊含义,如/d表示一位数字,而不是一个反斜线加上字母d。那反斜线怎么表示呢?答案是//。但在Java中,用//表示转义符号,一位数字在Java中表达为//d,而反斜线表示为////

在学习过程中,感觉量词是正则表达式里最不容易理解的地方,所以特别为量词写了一篇总结,点击这里可以查看


三、Java中的正则表达式工具

Java中的正则表达式工具主要集中在 java.util.regex 包中。我们可以用static Pattern.compile(String regex)方法传入我们的正则表达式来创建一个Pattren对象。我们可以把它看作是一个正则表达式对象。之后,我们可以调用Pattern对象matcher(String text)方法传入我们的待匹配文本,得到一个Matcher对象,这个对象可以用我们传入的正则表达式来对我们传入的文本进行各种匹配操作。当我们想处理另一段文本时,可以调用Matcher对象reset(String text)方法来将需处理文本重置为我们传入的文本。

另外,在String类中也包含了一些正则表达式的功能,如String类split(String regex)方法可以以传入的正则表达式为界将该字符串进行分割。

我们可以用Java来检验一下用<.*>去匹配<p>first</p> yeah的结果。下面是代码:

import java.io.*;
import java.util.regex.*;

public class RegexExample {
        public static void main(String[] args) {
                Pattern pattern = Pattern.compile("<.*>");
                Matcher matcher = pattern.matcher("<p>first</p> yeah");
                while(matcher.find()){
                        System.out.println(matcher.group());
                }
        }
}                                                                                                                                                      

输出为:
<p>first</p>

Pattern标记

Pattern类compile()方法还有以下版本。

Pattern Pattern.compile(String regex, int flag)

其中,flag参数接收一个标记参数,不同的标记参数,会有不同的匹配效果。

  • Pattern.CASE_INSENSITIVE(?i)。忽略大小写,默认情况下只能忽略US-ASCII字符集中字符的大小写。如同同时还指定了UNICODE_CASE标记,那么也能忽略基于Unicode字符集的字符的大小写。
  • Pattern.UNICODE_CASE(?u)。与CASE_INSENSITIVE结合使用,忽略基于Unicode字符集的字符的大小写。
  • Pattern.MULTILINE(?m)。使^$能匹配行首和行末,而不仅仅是字符串的开头和结尾
  • Pattern.COMMENTS(?x)。在此模式下,正则表达式中的空格符和从#开始到行末的注释都会被忽略掉。
  • Pattern.DOTALL(?s)。表达式.将能匹配所有字符,包括终结符。
  • Pattern.UNIX_LINES(?d)。在此模式下,.^$将只能识别行终结符。

其它还有Pattern.CANON_EQPattern.UNICODE_CHARACTER_CLASSPattern.LITERAL这些标记,大家可以在这里找到它们的信息。

下面是一个使用例子。

import java.io.*;
import java.util.regex.*;

public class RegexFlagExample {
        public static void main(String[] args) {
                Matcher matcher = Pattern.compile("^j av a", Pattern.CASE_INSENSITIVE | Pattern.COMMENTS).matcher("JaVa");
                while(matcher.find()){
                        System.out.println(matcher.group());
                }
        }
}

输出为:
JaVa

替换操作

跟许多的ctrl+f(查找)操作经常搭配替换一样,正则表达式也经常被用在替换操作上。Matcher类提供了许多替换的方法,像replaceFirst(String replacement)replaceAll(String replacement)appendReplacement(StringBuffer sbuf, String replacement)等。前两个很好理解,主要是第三个方法这里要讲一下。

appendReplacement(StringBuffer sbuf, String replacement)可以让我们为每个被匹配的字符串定制替换的内容。而前两个方法,每个被匹配的字符串都会被替换成相同的内容。下面看一个例子,在这个例子中,我们需要把每行开头的字符转化为大写形式。

import java.util.regex.*;

public class ReplaceExample {
        public static void main(String[] args) {
                Pattern pattern = Pattern.compile("^.", Pattern.MULTILINE);
                Matcher matcher = pattern.matcher("i like java\ni like regex");
                StringBuffer sbuf = new StringBuffer();
                while(matcher.find()){
                        matcher.appendReplacement(sbuf, matcher.group().toUpperCase());
                }
                matcher.appendTail(sbuf);
                System.out.println(sbuf);
        }
}

输出为:
I like java
I like regex

在这里,我们通过appendReplacement将每个组的变化,添加到了sbuf中,然后通过appendTail将文本其余的部分原封不动地放到了sbuf上,完成了整个替换工作。


四、参考资料

Java学习日常
Web note ad 1