正则表达式(Java版整理)

正则表达式(Java版整理)

基础

元字符

代码 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
^ 匹配字符串的开始
$ 匹配字符串的结束
\b 匹配字符串的结束

重复

代码/语法 说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

字符类

想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。
我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。

分枝条件

用|把不同的规则分别表达。
如:0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。

反义

代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

分组

重复单个字符直接在字符后面加上限定符就行了,但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作。
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:

  1. ((A)(B(C)))
  2. (A)
  3. (B(C))
  4. (C)

组零始终代表整个表达式。

之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用在表达式中使用,也可以在匹配操作完成后从匹配器获取。

与组关联的捕获输入始终是与组最近匹配的子序列。如果由于量化的缘故再次计算了组,则在第二次计算失败时将保留其以前捕获的值(如果有的话)例如,将字符串 "aba" 与表达式 (a(b)?)+ 相匹配,会将第二组设置为 "b"。在每个匹配的开头,所有捕获的输入都会被丢弃。

以 (?) 开头的组是纯的非捕获 组,它不捕获文本,也不针对组合计进行计数。

后向引用用于重复搜索前面某个分组匹配的文本。例如:
\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。

也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b

零宽断言

(?=exp)也叫零宽度正预测先行断言,它断言被匹配的字符串以表达式exp结尾但除了结尾以外的部分。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。
(?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。

代码/语法 说明
(?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置

注释

小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d \d?(?#0-199)

贪婪与懒惰

语法 说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可
能多的字符。考虑这个表达式:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的
话,它会匹配整个字符串aabab。这被称为贪婪匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,
只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用
最少的重复。现在看看懒惰版的例子吧:
a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字
符)和ab(第四到第五个字符)。

POSIX 字符类(仅 US-ASCII)

语法 说明
\p{Lower} 小写字母字符:[a-z]
\p{Upper} 大写字母字符:[A-Z]
\p{ASCII} 所有 ASCII:[\x00-\x7F]
\p{Alpha} 字母字符:[\p{Lower}\p{Upper}]
\p{Digit} 十进制数字:[0-9]
\p{Alnum} 字母数字字符:[\p{Alpha}\p{Digit}]
\p{Punct} 标点符号:`!"#$%&'()*+,-./:;<=>?@[]^_{ }~`
\p{Graph} 可见字符:[\p{Alnum}\p{Punct}]
\p{Print} 可打印字符:[\p{Graph}\x20]
\p{Blank} 空格或制表符:[ \t]
\p{Cntrl} 控制字符:[\x00-\x1F\x7F]
\p{XDigit} 十六进制数字:[0-9a-fA-F]
\p{Space} 空白字符:[ \t\n\x0B\f\r]

引用

语法 说明
\ Nothing,但是引用以下字符
\Q Nothing,但是引用所有字符,直到 \E
\E Nothing,但是结束从 \Q 开始的引用

如:\Q\w+\E表示字符串\w+而不是正则中的单词字符:[a-zA-Z_0-9]。

其他

语法 说明
\xhh 十六进制值为0xhh的字符
\uhhhh 十六进制表示为0xhhhh的Unicode字符
\t 制表符Tab
\n 换行符
\r 回车
\f 换页
\e 转义(Escape)

处理选项

上面介绍了几个选项如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。下面是Java中常用的正则表达式选项:

名称 说明
CASE_INSENSITIVE 匹配时区分大小写
MULTILINE 更改^$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)
DOTALL 在 dotall 模式中,表达式 . 可以匹配任何字符,包括行结束符。默认情况下,此表达式不匹配行结束符。
UNICODE_CASE 指定此标志后,由 CASE_INSENSITIVE 标志启用时,不区分大小写的匹配将以符合 Unicode Standard 的方式完成。默认情况下,不区分大小写的匹配假定仅匹配 US-ASCII 字符集中的字符。通过嵌入式标志表达式 (?u) 也可以启用 Unicode 感知的大小写折叠。指定此标志可能对性能产生影响。
CANON_EQ 启用规范等价。指定此标志后,当且仅当其完整规范分解匹配时,两个字符才可视为匹配。例如,当指定此标志时,表达式 "a\u030A" 将与字符串 "\u00E5" 匹配。默认情况下,匹配不考虑采用规范等价。不存在可以启用规范等价的嵌入式标志字符。指定此标志可能对性能产生影响。
UNIX_LINES 启用 Unix 行模式。在此模式中,.、^ 和 $ 的行为中仅识别 '\n' 行结束符。通过嵌入式标志表达式 (?d) 也可以启用 Unix 行模式。
LITERAL 指定此标志后,指定模式的输入字符串就会作为字面值字符序列来对待。输入序列中的元字符或转义序列不具有任何特殊意义。标志 CASE_INSENSITIVE 和 UNICODE_CASE 在与此标志一起使用时将对匹配产生影响。其他标志都变得多余了。不存在可以启用字面值解析的嵌入式标志字符。
UNICODE_CHARACTER_CLASS
COMMENTS 模式中允许空白和注释。此模式将忽略空白和在结束行之前以 # 开头的嵌入式注释。通过嵌入式标志表达式 (?x) 也可以启用注释模式。

JAVA

基本用法

Pattern pattern = Pattern.compile("\\ba\\w*\\b");
Matcher matcher = pattern.matcher("abcdab cccabcd aaacd");
int index = 0;
while (matcher.find()) {
    String res = matcher.group();
    System.out.println(index + ":" + res);
    index++;
}

\\ba\\w*\\b表示匹配以字母a为开头的单词。
Pattern.compile(regex)表示将给定的正则表达式编译到具有给定标志的模式中。
matcher(str)创建匹配给定输入与此模式的匹配器。
mather.find()尝试查找与该模式匹配的输入序列的下一个子序列。
此方法从匹配器区域的开头开始,如果该方法的前一次调用成功了并且从那时开始匹配器没有被重置,则从以前匹配操作没有匹配的第一个字符开始。
如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。
group() 返回由以前匹配操作所匹配的输入子序列。
打印结果:

image

不同

从上例中可以看出,Java中的正则表达式与之前所说不一致(多了一个),在其他语言中\\表示我想在正则表达式中插入一个普通的反斜线,请不要给它任何特殊的意义,而在Java中,\\的意思是我想要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。

如果想表示数字,则是\\d。如果要插入普通的反斜线,则是\\\\

String

String类中有几种方法可以使用正则表达式:

方法 返回类型 功能 示例
matches() boolean 告知此字符串是否匹配给定的正则表达式。 "-1234".matches("^-?\\d+$") => true
replaceAll(String regex, String replacement) String 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。 "a1b2c3".replaceAll("[a-zA-z]", "") => 123
replaceFirst(String regex, String replacement) String 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。 "Hello World! Hello Everyone!".replaceFirst("\\s", "") => HelloWorld! Hello Everyone!
split(String regex) String[] 根据给定正则表达式的匹配拆分此字符串。 "boo:and:foo".split(":") => { "boo", "and", "foo" }
split(String regex, int limit) String[] 根据给定正则表达式的匹配拆分此字符串。 "boo:and:foo".split(":", 5) => { "boo", "and", "foo" }

split(String regex, int limit)方法中limit 参数控制模式应用的次数,因此影响所得数组的长度。如果该限制 n 大于 0,则模式将被最多应用 n - 1 次,数组的长度将不会大于 n,而且数组的最后一项将包含所有超出最后匹配的定界符的输入。如果 n 为非正,那么模式将被应用尽可能多的次数,而且数组可以是任何长度。如果 n 为 0,那么模式将被应用尽可能多的次数,数组可以是任何长度,并且结尾空字符串将被丢弃。

例如,字符串 "boo:and:foo" 使用这些参数可生成以下结果:

Regex Limit 结果
: 2 { "boo", "and:foo" }
: 5 { "boo", "and", "foo" }
: -2 { "boo", "and", "foo" }
o 5 { "b", "", ":and:f", "", "" }
o -2 { "b", "", ":and:f", "", "" }
o 0 { "b", "", ":and:f" }

调用此方法的 str.split(regex, n) 形式与以下表达式产生的结果完全相同:Pattern.compile(regex).split(str, n)

java.util.regex

在regex包中,包括了两个类,Pattern(模式类)和Matcher(匹配器类)。Pattern类是用来表达和陈述所要搜索模式的对象,Matcher类是真正影响搜索的对象。另加一个新的例外类,PatternSyntaxException,当遇到不合法的搜索模式时,会抛出例外。

Pattern

  • 简介
    正则表达式的编译表示形式。

    指定为字符串的正则表达式必须首先被编译为此类的实例。然后,可将得到的模式用于创建 Matcher 对象,依照正则表达式,该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中,所以多个匹配器可以共享同一模式。

    因此,典型的调用顺序是

     Pattern p = Pattern.compile("a*b");
     Matcher m = p.matcher("aaaaab");
     boolean b = m.matches();
    

    在仅使用一次正则表达式时,可以方便地通过此类定义 matches 方法。此方法编译表达式并在单个调用中将输入序列与其匹配。语句

     boolean b = Pattern.matches("a*b", "aaaaab");
    

    等效于上面的三个语句,尽管对于重复的匹配而言它效率不高,因为它不允许重用已编译的模式。
    此类的实例是不可变的,可供多个并发线程安全使用。Matcher 类的实例用于此目的则不安全。

  • 常用方法

Pattern类中最重要的方法便是compilematcher,上面已经给出示例。下面看看其他方法:

方法 返回类型 功能 示例
flags() int 返回此模式的匹配标志。 Pattern.compile("\\w*", Pattern.MULTILINE).flags() => 8
pattern() String 返回在其中编译过此模式的正则表达式。 Pattern.compile("\\w*").pattern() => \w*
static quote(String s) String 返回指定 String 的字面值模式 String。 Pattern.quote("\\w+") => \Q\w+\E

Matcher

  • 简介

通过解释 Pattern 对 character sequence 执行匹配操作的引擎。

通过调用模式的 matcher 方法从模式创建匹配器。创建匹配器后,可以使用它执行三种不同的匹配操作:

  • matches 方法尝试将整个输入序列与该模式匹配。

  • lookingAt 尝试将输入序列从头开始与该模式匹配。

  • find 方法扫描输入序列以查找与该模式匹配的下一个子序列。

每个方法都返回一个表示成功或失败的布尔值。通过查询匹配器的状态可以获取关于成功匹配的更多信息。

匹配器在其输入的子集(称为区域)中查找匹配项。默认情况下,此区域包含全部的匹配器输入。可通过 region 方法修改区域,通过 regionStart 和 regionEnd 方法查询区域。区域边界与某些模式构造交互的方式是可以更改的。

此类还定义使用新字符串替换匹配子序列的方法,需要时,可以从匹配结果计算出新字符串的内容。可以先后使用 appendReplacement 和 appendTail 方法将结果收集到现有的字符串缓冲区,或者使用更加便捷的 replaceAll 方法创建一个可以在其中替换输入序列中每个匹配子序列的字符串。

匹配器的显式状态包括最近成功匹配的开始和结束索引。它还包括模式中每个捕获组捕获的输入子序列的开始和结束索引以及该子序列的总数。出于方便的考虑,还提供了以字符串的形式返回这些已捕获子序列的方法。

匹配器的显式状态最初是未定义的;在成功匹配导致 IllegalStateException 抛出之前尝试查询其中的任何部分。每个匹配操作都将重新计算匹配器的显式状态。

匹配器的隐式状态包括输入字符序列和添加位置,添加位置最初是零,然后由 appendReplacement 方法更新。

可以通过调用匹配器的 reset() 方法来显式重置匹配器,如果需要新输入序列,则调用其 reset(CharSequence) 方法。重置匹配器将放弃其显式状态信息并将添加位置设置为零。

此类的实例用于多个并发线程是不安全的。

  • 常用方法

matches()
表示字符串完全符合给出的正则表达式所表示的范围。只要有一个字符不匹配则返回false。如:

Pattern.matches("[a-z]", "aAbBcC")

返回false,因为正则表达式表示的范围不包含大写字母。

find()
find()尝试查找与该模式匹配的输入序列的下一个子序列。
此方法从匹配器区域的开头开始,如果该方法的前一次调用成功了并且从那时开始匹配器没有被重置,则从以前匹配操作没有匹配的第一个字符开始。
如果匹配成功,则可以通过 start、end 和 group 方法获取更多信息。

    Pattern pattern = Pattern.compile("[a-z]");
    Matcher matcher = pattern.matcher("aAbBcC");
    matcher.find();

返回true,因为可以匹配到小写字母。

需要注意的是在执行find()方法时,其内部指针会跟着变动,比如第一次调用完毕,此时的matcher.start()为0,因为第一个字母就匹配上了,而matcher.end()则返回2,因为它返回的是最后不匹配(A)的位置的下一个索引,因此可以通过如下方法看到指针移动轨迹:

        Pattern p = Pattern.compile("\\d{3,5}");
        String s = "123-34345-234-00";
        while (m.find()) {
            System.out.println(m.start() + "-" + m.end());
        }

打印结果:


image

可以看到,find方法比较实诚,善始善终,但我们有时候需要人工插手而不总是从头到尾来一遍,这就涉及到find方法的另一个多态形式了。

find(int start)
从指定位置开始匹配,使用此方法模拟find():

        int index = 0;
        while (m.find(index)) {
            System.out.println(m.start() + "-" + m.end());
            index = m.end();
        }

lookingAt()
此方法总是从头开始匹配,无论是否匹配上均立即返回相应结果,并且不再继续匹配,语言乏力,此处使用find方法模拟:

    public static void main(String[] args) throws Exception {
        Pattern p = Pattern.compile("\\d{3,5}");
        String s = "123-34345-234-00";
        Matcher m = p.matcher(s);

        System.out.println(cosplayMethodLookingAt(m));
        System.out.println(cosplayMethodLookingAt(m));
        System.out.println(cosplayMethodLookingAt(m));
        System.out.println(cosplayMethodLookingAt(m));
    }

    private static boolean cosplayMethodLookingAt(Matcher m) {
        boolean res = m.find() && m.start() == 0;
        m.reset();
        return res;
    }

显示效果与lookingAt方法相同,但内部实现不一样:

    public static void main(String[] args) throws Exception {
        Pattern p = Pattern.compile("\\d{3,5}");
        String s = "123-34345-234-00";
        Matcher m = p.matcher(s);

        m.find();
        m.lookingAt();
        m.find();
        // 4
        System.out.println(m.start());
        cosplayMethodLookingAt(m);
        m.find();
        System.out.println(m.start());
    }

    private static boolean cosplayMethodLookingAt(Matcher m) {
        boolean res = m.find() && m.start() == 0;
        m.reset();
        return res;
    }

打印结果:


image

由此可见,lookingAt方法并不会重置匹配使用的内部指针。

appendReplacement
public Matcher appendReplacement(StringBuffer sb, String replacement)

实现非终端添加和替换步骤。
此方法执行以下操作:

它从添加位置开始在输入序列读取字符,并将其添加到给定字符串缓冲区。在读取以前匹配之前的最后字符(即位于索引 start() - 1 处的字符)之后,它就会停止。

它将给定替换字符串添加到字符串缓冲区。

它将此匹配器的添加位置设置为最后匹配位置的索引加 1,即 end()。

替换字符串可能包含到以前匹配期间所捕获的子序列的引用:<nobr aria-hidden="true" style="margin: 0px; padding: 0px; transition: none 0s ease 0s; border: 0px; max-width: none; max-height: none; min-width: 0px; min-height: 0px; vertical-align: 0px; line-height: normal; text-decoration: none; white-space: nowrap !important;">g每次出现时,都将被group(g)的计算结果替换。</nobr><math xmlns="http://www.w3.org/1998/Math/MathML"><mi>g</mi><mrow class="MJX-TeXAtom-ORD"><mo>每</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>次</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>出</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>现</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>时</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>,</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>都</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>将</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>被</mo></mrow><mi>g</mi><mi>r</mi><mi>o</mi><mi>u</mi><mi>p</mi><mo stretchy="false">(</mo><mi>g</mi><mo stretchy="false">)</mo><mrow class="MJX-TeXAtom-ORD"><mo>的</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>计</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>算</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>结</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>果</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>替</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>换</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>。</mo></mrow></math> 之后的第一个数始终被视为组引用的一部分。如果后续的数可以形成合法组引用,则将被合并到 g 中。只有数字 '0' 到 '9' 被视为组引用的可能组件。例如,如果第二个组匹配字符串 "foo",则传递替换字符串 "<nobr aria-hidden="true" style="margin: 0px; padding: 0px; transition: none 0s ease 0s; border: 0px; max-width: none; max-height: none; min-width: 0px; min-height: 0px; vertical-align: 0px; line-height: normal; text-decoration: none; white-space: nowrap !important;">2bar"将导致"foobar"被添加到字符串缓冲区。可能将美元符号(</nobr><math xmlns="http://www.w3.org/1998/Math/MathML"><mn>2</mn><mi>b</mi><mi>a</mi><mi>r</mi><mo>"</mo><mrow class="MJX-TeXAtom-ORD"><mo>将</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>导</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>致</mo></mrow><mo>"</mo><mi>f</mi><mi>o</mi><mi>o</mi><mi>b</mi><mi>a</mi><mi>r</mi><mo>"</mo><mrow class="MJX-TeXAtom-ORD"><mo>被</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>添</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>加</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>到</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>字</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>符</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>串</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>缓</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>冲</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>区</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>。</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>可</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>能</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>将</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>美</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>元</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>符</mo></mrow><mrow class="MJX-TeXAtom-ORD"><mo>号</mo></mrow><mo stretchy="false">(</mo></math>) 作为替换字符串中的字面值(通过前面使用一个反斜线 ($))包括进来。

注意,在替换字符串中使用反斜线 () 和美元符号 ($) 可能导致与作为字面值替换字符串时所产生的结果不同。美元符号可视为到如上所述已捕获子序列的引用,反斜线可用于转义替换字符串中的字面值字符。

此方法设计用于循环以及 appendTail 和 find 方法中。例如,以下代码将 one dog two dogs in the yard 写入标准输出流中:

 Pattern p = Pattern.compile("cat");
 Matcher m = p.matcher("one cat two cats in the yard");
 StringBuffer sb = new StringBuffer();
 while (m.find()) {
     m.appendReplacement(sb, "dog");
 }
 m.appendTail(sb);
 System.out.println(sb.toString());

输出结果:


image

appendTail

StringBuffer appendTail(StringBuffer sb)
此方法从添加位置开始从输入序列读取字符,并将其添加到给定字符串缓冲区。可以在一次或多次调用 appendReplacement 方法后调用它来复制剩余的输入序列。

以上例来说,当匹配到第二个cat时,while语句块中的代码就失效了,此时后面的字符串s in the yard就需要使用appendTail方法来补齐,否则输出结果就是:

image

显然有所缺失。

group

String group()
返回由以前匹配操作所匹配的输入子序列。
对于具有输入序列 s 的匹配器 m,表达式 m.group() 和 s.substring(m.start(), m.end()) 是等效的。
注意,某些模式(例如,a*)匹配空字符串。当模式成功匹配输入中的空字符串时,此方法将返回空字符串。

group(int group)

String group(int group)
返回在以前匹配操作期间由给定组捕获的输入子序列。
对于匹配器 m、输入序列 s 和组索引 g,表达式 m.group(g) 和 s.substring(m.start(g), m.end(g)) 是等效的。

捕获组是从 1 开始从左到右的索引。组零表示整个模式,因此表达式 m.group(0) 等效于 m.group()。

如果该匹配成功了,但指定组未能匹配输入序列的任何部分,则返回 null。注意,某些组(例如,(a*))匹配空字符串。当这些的组成功匹配输入中的空字符串时,此方法将返回空字符串。

        Pattern p = Pattern.compile("\\b\\w+(o\\w+)\\b");
        Matcher m = p.matcher("i love you");
        while (m.find()) {
            System.out.println("整个匹配结果=>"+m.group());
            System.out.println("一组匹配结果=>"+m.group(1));
        }

输出结果:

image

其他

其他方法要么可以融会贯通,比如start(int),要么就是不常用,比如hasAnchoringBounds(),因此在需要的时候可以查看中文文档。
奉上连接:Matcher

常用正则

校验数字的表达式

数字:^[0-9]*$
n位的数字:^\d{n}$
至少n位的数字:^\d{n,}$
m-n位的数字:^\d{m,n}$
零和非零开头的数字:^(0|[1-9][0-9]*)$
非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$
正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
非零的正整数:^[1-9]\d*$^([1-9][0-9]*){1,3}$^\+?[1-9][0-9]*$
非零的负整数:^\-[1-9][]0-9″*$^-[1-9]\d*$
非负整数:^\d+$^[1-9]\d*|0$
非正整数:^-[1-9]\d*|0$^((-\d+)|(0+))$
非负浮点数:^\d+(\.\d+)?$^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
浮点数:^(-?\d+)(\.\d+)?$^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$

校验字符的表达式

汉字:^[\u4e00-\u9fa5]{0,}$
英文和数字:^[A-Za-z0-9]+$^[A-Za-z0-9]{4,40}$
长度为3-20的所有字符:^.{3,20}$
由26个英文字母组成的字符串:^[A-Za-z]+$
由26个大写英文字母组成的字符串:^[A-Z]+$
由26个小写英文字母组成的字符串:^[a-z]+$
由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
由数字、26个英文字母或者下划线组成的字符串:^\w+$^\w{3,20}$
中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
可以输入含有^%&',;=?$\”等字符:[^%&',;=?$\x22]+
禁止输入含有~的字符:[^~\x22]+

特殊需求表达式

Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL:[a-zA-z]+://[^\s]*^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
电话号码(“XXX-XXXXXXX”、”XXXX-XXXXXXXX”、”XXX-XXXXXXX”、”XXX-XXXXXXXX”、”XXXXXXX”和”XXXXXXXX):^($$\d{3,4}-)|\d{3.4}-)?\d{7,8}$
国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
身份证号(15位、18位数字):^\d{15}|\d{18}$
短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
日期格式:^\d{4}-\d{1,2}-\d{1,2}
一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$

xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
中文字符的正则表达式:[\u4e00-\u9fa5]
双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式:\n\s*\r (可以用来删除空白行)
HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
首尾空白字符的正则表达式:^\s*|\s*$(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
IP地址:((25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}\2

钱的输入格式

有四种钱的表示形式我们可以接受:”10000.00″ 和 “10,000.00″, 和没有 “分” 的 “10000″ 和 “10,000″:^[1-9][0-9]*$
这表示任意一个不以0开头的数字,但是,这也意味着一个字符”0″不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
必须说明的是,小数点后面至少应该有1位数,所以”10.”是不通过的,但是 “10″ 和 “10.2″ 是通过的:^[0-9]+(.[0-9]{2})?$
这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
这样就允许用户只写一位小数。下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
备注:这就是最终结果了,别忘了”+”可以用”*”替代。如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 初衷:看了很多视频、文章,最后却通通忘记了,别人的知识依旧是别人的,自己却什么都没获得。此系列文章旨在加深自己的印...
    DCbryant阅读 3,896评论 0 20
  • 九、正则表达式 原文:Regular Expressions译者:飞龙协议:CC BY-NC-SA 4.0自豪地采...
    布客飞龙阅读 931评论 0 5
  • 正则表达式到底是什么东西?字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等...
    狮子挽歌阅读 2,104评论 0 9
  • 给宝宝画了一个卡卡,虽然没有正儿八经学过画画,这已经是我的最高水平了……
    睿小宝课堂阅读 156评论 0 0
  • 又快中秋了,原来秋已过半。每年过了中秋,就会觉得这一年也快过去了似的。近来有些匆忙,节奏稍快了些,感觉人也有...
    我是敏子阅读 339评论 1 3