概念

正则表达式(regex)是一个字符串,由字面值字符和特殊符号组成,是用来描述匹配一个字符串集合的模式,可以用来匹配、替换和拆分字符串。

经常用在检查一个字符串是否含有某种子字符串、将匹配的子字符串做替换或者从某个字符串中取出符合某个条件的子字符串等。

语法

在其他语言中,\\表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。

Java中,\\表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。

所以,在其他的语言中(如Perl),一个反斜杠\就足以具有转义的作用,而在Java中正则表达式中则需要有两个反斜杠才能被解析为其他语言中的转义作用。也可以简单的理解在Java的正则表达式中,两个\\代表其他语言中的一个\,这也就是为什么表示一位数字的正则表达式是\\d,而表示一个普通的反斜杠是\\

比如:System.out.print("\\"); // 输出为 \System.out.print("\\\\"); // 输出为 \\

字符

说明

\

将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如, n匹配字符 n。\n 匹配换行符。序列 \\ 匹配 \ ,\( 匹配 (。

^

匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与"\n"或"\r"之后的位置匹配。

$

匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。

*

零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,}。

+

一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}。

?

零次或一次匹配前面的字符或子表达式。例如,"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1}。

{n}

n 是非负整数。正好匹配 n 次。例如,"o{2}"与"Bob"中的"o"不匹配,但与"food"中的两个"o"匹配。

{n,}

n 是非负整数。至少匹配 n 次。例如,"o{2,}"不匹配"Bob"中的"o",而匹配"foooood"中的所有 o。"o{1,}"等效于"o+"。"o{0,}"等效于"o*"。

{n,m}

m 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,"o{1,3}"匹配"fooooood"中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。

?

当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是"非贪心的"。"非贪心的"模式匹配搜索到的、尽可能短的字符串,而默认的"贪心的"模式匹配搜索到的、尽可能长的字符串。例如,在字符串"oooo"中,"o+?"只匹配单个"o",而"o+"匹配所有"o"。

.

匹配除"\r\n"之外的任何单个字符。若要匹配包括"\r\n"在内的任意字符,请使用诸如"[\s\S]"之类的模式。

x

y

[xyz]

字符集。匹配包含的任一字符。例如,"[abc]"匹配"plain"中的"a"。

[^xyz]

反向字符集。匹配未包含的任何字符。例如,"[^abc]"匹配"plain"中"p","l","i","n"。

[a-z]

字符范围。匹配指定范围内的任何字符。例如,"[a-z]"匹配"a"到"z"范围内的任何小写字母。

[^a-z]

反向范围字符。匹配不在指定的范围内的任何字符。例如,"[^a-z]"匹配任何不在"a"到"z"范围内的任何字符。

\b

匹配一个字边界,即字与空格间的位置。例如,"er\b"匹配"never"中的"er",但不匹配"verb"中的"er"。

\B

非字边界匹配。"er\B"匹配"verb"中的"er",但不匹配"never"中的"er"。

\d

数字字符匹配。等效于 [0-9]。

\D

非数字字符匹配。等效于 [^0-9]。

\s

匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。

\S

匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。

\w

匹配任何字类字符,包括下划线。与"[A-Za-z0-9_]"等效。

\W

与任何非单词字符匹配。与"[^A-Za-z0-9_]"等效。

(?:pattern)

匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用"or"字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。

(?=pattern)

执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

(?!pattern)

执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配"Windows 3.1"中的 "Windows",但不匹配"Windows 2000"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

常用案例

  • 整数或者小数:^[0-9]+\.{0,1}[0-9]{0,2}$
  • 英文字母:^[A-Za-z]+$
  • 汉字:^[\u4e00-\u9fa5]{0,}$
  • 邮箱:^\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})+/.?
  • url:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
  • 手机号码:^1[35789]\d{9}$
  • 校验IP-v4地址:\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b
  • 校验IP-v6地址:(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))
  • 身份证号码15位:^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$
  • 身份证号码18位:^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$

在线正则表达式测试: 

JAVA中如何使用

Pattern类与Matcher类

Java的正则表达式是由java.util.regexPatternMatcher类实现的。Pattern对象表示经编译的正则表达式。静态的compile()方法负责将表示正则表达式的字符串编译成Pattern对象。

Matcher类的方法

索引方法提供了有用的索引值,精确表明输入字符串中在哪能找到匹配:

public int start()
返回以前匹配的初始索引。

public int start(int group)
返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引。

public int end()
返回最后匹配字符之后的偏移量。

public int end()
返回最后匹配字符之后的偏移量。

查找方法用来检查输入字符串并返回一个布尔值,表示是否找到该模式:

public boolean lookingAt()
尝试将从区域开头开始的输入序列与该模式匹配。

public boolean lookingAt()
尝试将从区域开头开始的输入序列与该模式匹配。

public boolean lookingAt()
尝试将从区域开头开始的输入序列与该模式匹配。

替换方法是替换输入字符串里文本:

public Matcher appendReplacement(StringBuffer sb, String replacement)
实现非终端添加和替换步骤。

public StringBuffer appendTail(StringBuffer sb)
实现终端添加和替换步骤。

public String replaceAll(String replacement)
替换模式与给定替换字符串相匹配的输入序列的每个子序列。

public String replaceFirst(String replacement)
替换模式与给定替换字符串匹配的输入序列的第一个子序列。

public static String quoteReplacement(String s)
返回指定字符串的字面替换字符串。这个方法返回一个字符串,就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。

案例

判断是否以某种字符开始或结尾:

String str ="test@163.com";
  System.out.println(str.startsWith("test"));
  System.out.println(str.endsWith(".com"));

手机号码、邮箱账号匹配:

String reg1 ="^1[35789]\\d{9}$";
  String phone ="13588888888";
  Pattern pattern1 = Pattern.compile(reg1);
  Matcher matcher1 = pattern1.matcher(phone);
  System.out.println(matcher1.matches());

  String reg2 ="^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
  String emails ="test@163.com";
  Pattern pattern2 = Pattern.compile(reg2);
  Matcher matcher2 = pattern2.matcher(emails);
  System.out.println(matcher2.matches());

手机号码、邮箱账号脱敏:

String phone = "13588888888";
  System.out.println(phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
  //135****8888
  
  String emails = "test@163.com";
  System.out.println(emails.replaceAll("(\\w?)(\\w+)(\\w)(@\\w+\\.[a-z]+(\\.[a-z]+)?)", "$1****$3$4"));
  //t****t@163.com

日期字符串格式化:

String reg = "(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})";
  String time = "20230228095440";
  System.out.println(time.replaceAll(reg, "$1-$2-$3 $4:$5:$6"));
  //2023-02-28 09:54:40

正则匹配多个关键字:

public static void main(String[] args) {
        
        /**
        * (?=.*example) :查找 URL 中是否包含 "example" 关键字 
        * (?=.*org) :查找 URL 中是否包含 "org" 关键字 
        * (?=.*html) :查找 URL 中是否包含 "html" 关键字 
        * .* :匹配 URL 中的任意字符 
        * 这个正则表达式会从头到尾匹配整个 URL,只有当 URL 中同时包含了 "example"、"org" 和 "html" 这三个关键字时,才会匹配成功。
        */

        Pattern pattern = Pattern.compile(patternString);
        Matcher matcher = pattern.matcher(url);
        boolean isMatch = matcher.matches();
        if (isMatch) {
            System.out.println("URL matched");
        } else {
            System.out.println("URL not matched");
        }

        System.out.println("==============================");
        
        /**
        * 其中,String.join("|", keywords) 将多个关键字用 | 连接起来,表示匹配其中任意一个关键字。
        * matcher.find() 则是在 url 中查找第一个匹配的关键字,如果匹配成功,就会返回 true。
        * matcher.group() 则是返回匹配的字符串。如果没有匹配成功,就会返回 false。
        */
        String url2 = "https://www.example.com/search?q=java+programming+tutorial";
        String[] keywords = {"java", "programming", "tutorial"};
        String pattern2 = String.join("|", keywords);
        Matcher matcher2 = Pattern.compile(pattern2).matcher(url2);

        if (matcher2.find()) {
            System.out.println("匹配成功!关键字为:" + matcher2.group());
        } else {
            System.out.println("未匹配到任何关键字。");
        }
    }