Regular Expressions

1 字符

B 指定字符B(没有反斜杠) \n 换行
\xhh 十六进制值为 oxhh 的字符 \r 回车
\uhhh 十六进制为 oxhhh 的Unicode字符 \f 换页
\e 转义(ESC) \t 制表符

2 字符类

常用的字符类形式:

. 任意字符 \s 空白符(包括 \n \r \t \f “ “)
[abc] 包含a b c的任意字符(等同于abc之间的或) \S 非空白符,即除了上述五种空白符之外的字符
[^abc] 包含除了a b c之外的任意字符 \d 数字,[0-9]
[a-zA-Z] 包含a-z或A-Z的任意字符 \D 非数字
[abc[de]] 类似于abcde之间的或 \w 词字符,即字母和数字,[a-zA-Z0-9]
[a-z&&[de]] 类似于(a-z)&&de,即包含d e的任意字符 \W 非字母和非数字的字符

Java中的正则表达式,需要使用额外的一个右斜杠对右斜杠进行转义,所以一般在Java的正则表达式中会看到\\d+[.]\\d+[.]\\d+[.]\\d+的情况,表达的意思就是类似于IP地址格式的字符串。

3 边界匹配符

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

4 量词

量词描述的是一个模式吸收输入文本的方式(好难懂…)

简单一点讲就是,量词在正则表达式中的位置,将会影响正则表达式对输入文本的匹配结果,是匹配单个还是多个,量词将会起到重要的作用。

(1)贪婪型

量词的默认属性,也是量词最基本的使用方法。所谓“贪婪”,即被该属性的量词描述的时候,正则表达式将会尽可能多地匹配,而不是遇到第一个匹配的就停止。

(2)勉强型

若要限定正则表达式匹配到最少的字符数,可以使用勉强型的量词修饰。勉强型的量词,即在贪婪型的基础上,多加一个问号“?”

(3)占有型

目前,占有型量词修饰的正则表达式,只能在Java中使用,由于比较高级,基本不会使用到。占有型量词,只需要在贪婪型的基础上,多加一个加号“+”

由于正则表达式在匹配的过程中,会产生许多的中间状态,以便在匹配失败后回溯到之前成功的状态,这些中间状态过多会引起正则表达式的失控以及效率低下,所以占有型的目的就是通过不保存中间状态的方式,提高正则表达式的效率。

(4)附:贪婪型量词描述

量词的转义,需要连续的两个右斜杠 \\

X? 一个或者零个X
X* 零个或者多个X
X+ 一个或者多个X
X{n} n个X
X{n,} 至少n个X
X{n,m} X的出现次数是n-m之间

5 Pattern类与Matcher类

(1)Pattern类的使用

可以单纯使用String类提供的方法分割或者替换字符串中的字符,如果觉得不能满足需求,也可以通过Pattern类自定义正则表达式对象,用于匹配字符串。

  • 导入Pattern类所在的包,java.util.regex

  • 写好正则表达式regex之后,使用Pattern类的静态方法Pattern.compile(String regex)编译正则表达式,生成一个Pattern类对象;

  • 将所需匹配的原字符串作为参数,传递给Pattern类的方法matcher(String str),生成一个Matcher类对象,之后就可以使用Matcher类的方法,对原字符串进行匹配后的操作,包括分割、替换、查询匹配的起止位置等。

    (具体的Matcher类方法可以查阅JDK文档的java.util.regex.Matcher

(2)组(Group)的概念

组,使用括号划分的正则表达式,例如A(BC(DE(GH)F)),可以根据组的编号来引用某一个组,组号为0表示整个表达式,从左往右遇到的第一个括号包含的部分是组号为1的正则表达式,以此类推。

利用组的概念以及相应的方法,可以在匹配成功之后(可以通过Matcher.find()方法返回的true/false,判断是否匹配成功),定位到相应的位置,确定字符串到底是与哪一个组匹配上了。

Matcher.group(),即返回当前匹配到的字符串,如果将该方法放在一个循环中,则会依次输出匹配到的字符串。

关于判断是否匹配成功,有很多种方法:

  • boolean matches();,只有在整个输入都匹配正则表达式的时候,才返回true
  • boolean lookingAt();,只有在输入的一开始就匹配正则表达式的时候,才返回true
  • boolean find();,输入在任意位置与正则表达式匹配,都返回true

(3)CharSequence——正则表达式在Java中的数据类型

接口CharSequence从CharBuffer、String、StringBuffer、StringBuilder类中抽象出字符序列的一般化定义:

1
2
3
4
5
6
interface CharSequence {
charAt(int i);
length();
subSequence(int start, int end);
toString();
}

上述的类都实现了该接口,多数正则表达式的操作都接受CharSequence类型的参数。

(4)Pattern类的标记

Pattern类的标记用于稍微改变匹配过程中的匹配规则和方式。

Pattern类的标记在Pattern.compile(String regex, int mark)的时候,可以引入。

比较有用的有三种:

  • Pattern.CASE_INSENSITIVE,不考虑字母的大小写
  • Pattern.COMMENTS,忽略空格符
  • Pattern.MULTILINE,默认模式下,将会匹配完整的字符串,不管有多少行,该模式下,则可以根据^和$的标记,按行为单位进行匹配

(5)分割字符串方法split()

split()方法可以将输入的字符串按照匹配的情况,分割成字符串对象数组

String[] split(CharSequence input)

String[] split(CharSequence input, int limit),可以限制将输入分割成子串的数量

(6)渐进式替换字符串方法appendRepalcement()

在字符串自带的方法之上,若想实现渐进式的字符串替换方式(在替换原字符串匹配部分的过程中,还可以调整用于替换的字符串),则需要使用Matcher.appendReplacement(StringBuffer strf, String replacement)方法。strf是用于保存最终的结果,replacement部分可以在替换的过程中通过自定义而变化,最后使用Matcher.appendTail(StringBuffer strf)方法可以将最终的结果全部存储到strf中,用于后续的输出。

(7)重复使用Matcher对象的方法reset()

通过一系列的方法,得到了一个Matcher对象,在匹配了一个字符串之后,如果还想将其应用于其他的字符串,该怎么办?重新创建?不,可以使用Matcher.reset(String newStr)方法,从而可以重复地使用之前创建的Matcher对象。

若使用不带参数的Matcher.reset()方法,则表示将Matcher对象重新设置到当前序列的起始位置

6 正则表达式与Java I/O

  • 可以利用net.mindview.util.TextFile对象将文件打开,并将结果传递给一个字符串对象:String file = TextFile.read("filename")

  • StringReader将String转换为可读的流对象,然后使用流对象构造BufferReader对象,为什么呢?因为我们需要使用BufferReader的readLine()方法读取输入的文本。

    如果其中涉及需要将字符串转换成相应的基本数据类型,可以使用相应基本类型的包装类的方法进行转换:Integer.parseInt(String str)/Double.parseDouble(String str)/...

  • Scanner类用于扫描输入的字符串。Scanner类的构造器可以接受任何类型的输入对象,包括File对象 InputStream对象 String对象 Readable对象。在Scanner中,所有的输入、分词以及翻译的操作都可以隐藏在不同类型的next()方法中:next()方法将会简单地返回下一个String对象,nextInt()则会返回下一个整数,nextDouble()则会返回下一个Double类型的数等等。

    带参数的hasNext(String regex)方法和带参数的next(String regex)方法联合使用,可以用于寻找下一个匹配的字符串,然后使用Scanner.match()方法,可以返回一个匹配结果MatchResult对象,通过MatchResult.group(int i)方法获得匹配各组的字符串。

  • StringTokenizer

    在Scanner出现之前,用于分词的类,不过现在基本不会再使用了。

附:

Java的正则表达式

有关Java中正则表达式的完整构造子列表,可以参考JDK文档java.util.regex包中的Pattern类。