正则表达式-基础

正则表达式-基础

前言

正则表达式,是一种特殊的字符串模式,用来匹配一系列符合某个规则的字符串。就好比用模具做产品,而正则就是这个模具,定义一种规则去匹配符合规则的字符串。

正则的意思是正规、规则。正则表达式的英文名是 Regular Expression,可以直译为描述某种规则的表达式,一般缩写为 regex。正则表达式是从左往右匹配的。

初窥门径

我们先给出一个很常见的例子,判断一个字符串是否为一个有效的电话号码。

正常的思路是先判断字符串是否为11位,再判断每一为是否为数字即可。

public static boolean isValidTelphone(String number) {
    //判断长度为11位
    if(number.length() != 11) {
        return false;
    }
    //判断是否为数字
    for(int i=0;i<number.length();i++) {
        if(number.charAt(i)<'0' || number.charAt(i)>'9') {
            return false;
        }
    }
    return true;
}

使用正则表达式

public static boolean isValidTelphone(String number) {
    return number.matches("\\d{11}");
}

这个例子告诉我们,使用正则表达式的代码实现相当简洁高效。当然我们不急着讲解这个例子的正则表达式是如何构造的,继续学习之后自然也就明白了。

我们先来讲讲正则表达式的精确匹配,一个普通的字符串,例如 a b c abc abc,它如果用来做正则表达式匹配的话,只能匹配它自己。也就是说它只能匹配 a b c abc abc,不能匹配 a b ab ab 或者 A b c Abc Abc等其他任何字符串。

System.out.println("abc".matches("abc"));   //输出true
System.out.println("abc".matches("ab"));    //输出false
System.out.println("abc".matches("Abc"));   //输出false

事实上正则表达式的精确匹配很少用到,但它作为一条基本规则应该被了解。

对于精确匹配,实际上就是判断字符串相等,我们使用 Java 语言的 String.equals()方法同样也可以实现。

如果需要匹配的字符串含有特殊字符,就需要用 \ 进行转义。比如 a&b,进行正则表达式匹配时,需要使用 a\ &b,又因为 \ 也是特殊字符,它也需要转义,所以 a\ &b 对应的 Java 字符串是 a\ \ &b,它是用来匹配 a&b 的。

System.out.println("a&b".matches("a\\&b"));	//输出true

请记住,这两个反斜杠的意义并不一样,一个是正则的转义,一个是字符串的转义。

回到我们最开始的判断电话号码的例子,\ \d 的本意其实就是 \d。 \d 在正则表达式中表示匹配任意数字 ,d 是 digital 的意思。比如 00\d 就可以匹配 000、007、008 等。注意一下,\d 只能匹配单个数字。我们不可以使用 00\d 去匹配 0066 。

那么问题来了,怎么才能匹配多个数字呢?

  • 如果数字不多的话,我们可以写多次 \d。比如 \d\d 能匹配两个数字,\d\d\d 能匹配三个数字。
System.out.println("1".matches("\\d\\d"));		//输出false
System.out.println("11".matches("\\d\\d"));		//输出true
System.out.println("111".matches("\\d\\d\\d"));	//输出true
  • 如果数字太多的话,我们在 \d 后面打上花括号 { },{n}表示匹配 n 次 ,\d{10000} 就表示匹配10000个数字,
System.out.println("123456789".matches("\\d{10}"));		//输出false
System.out.println("1234567890".matches("\\d{10}"));	//输出true

如果要匹配 n 至 m 次,用 {n,m} 即可。如果要至少匹配 n 次,用{n,} 。注意“,”之后没有空格。

System.out.println("1".matches("\\d{1,2}"));	//输出true
System.out.println("12".matches("\\d{1,2}"));	//输出true
System.out.println("123".matches("\\d{1,2}"));	//输出false
System.out.println("1234".matches("\\d{2,}"));	//输出true

如果要至多匹配 m 次,用 {0,m} 即可 。千万不要想当然地使用 {,m},只是因为正无穷不好表示我们才用的 {n,},实际上根本没有 {,m} 这样的写法。

现在你应该完全理解了判断电话号码的例子,让我们继续进行学习。

渐入佳境

正则的基础规则中,除了 \d,还有 \w 和 \s。

  • w是 w o r d word word 的缩写,匹配一个常用字符,包括字母、数字、下划线
  • s是 s p a c e space space 的缩写,匹配一个空白字符,包括空格键打出来的空格,以及 \t \n \r \f
System.out.println("LeetCode_666".matches("\\w{12}"));						//输出true
System.out.println("\t \n".matches("\\s{3}"));								//输出true
System.out.println("Leet\tCode 666".matches("\\w{4}\\s\\w{4}\\s\\d{3}"));	//输出true

记住上面的三个规则之后,我们可以顺带记住几个新规则。因为在正则表达式中,把字母换成大写,就表示相反的意思

  • 用 \d 表示匹配一个数字,\D 则表示匹配一个非数字的字符
  • 用 \w 表示匹配一个常用字符,\W 则表示匹配一个不是字母、数字、下划线的字符
  • 用 \s 表示匹配一个空白字符,\S 则表示匹配一个非空白字符
System.out.println("1".matches("\\D"));		//输出false
System.out.println("*".matches("\\D"));		//输出true
System.out.println("b".matches("\\W"));		//输出false
System.out.println("&".matches("\\W"));		//输出true
System.out.println("\r".matches("\\S"));	//输出false
System.out.println("%".matches("\\S"));		//输出true

有时候,我们对某些位置的字符没有要求,仅仅需要占个位置而已,我们可以使用 . . . 字符,可以理解为 . . . 能够匹配任意字符

System.out.println("a0b".matches("a.b"));		//输出true
System.out.println("a_b".matches("a.b"));		//输出true
System.out.println("a b".matches("a.b"));		//输出true

同样地,有时候我们对匹配次数也没有要求,匹配任意次均可。这时,我们使用 * 字符。 ∗ * 是指可以匹配任意次,包括 0 次,也就是说, ∗ * 等价于 ${0,}

System.out.println("1234".matches("\\d*"));	//输出true
System.out.println("1".matches("\\d*"));	//输出true
System.out.println("".matches("\\d*"));		//输出true

如果要判断某个字符至少出现一次,那就可以使用 + + + 字符进行匹配, + + + 表示至少匹配一次,等价于 1 , {1,} 1

System.out.println("1234".matches("\\d+"));	//输出true
System.out.println("1".matches("\\d+"));	//输出true
System.out.println("".matches("\\d+"));		//输出false

还有一种场景,如果某个字符要么匹配 0 次,要么匹配 1 次,我们就可以用 ? ? ? 匹配,它等价于 0 , 1 {0,1} 01

System.out.println("".matches("\\d?"));		//输出true
System.out.println("1".matches("\\d?"));	//输出true
System.out.println("1234".matches("\\d?"));	//输出false

请一定要记住这些规则哦。

  • . . . 表示匹配任意字符
  • ∗ * 匹配任意次,包括 0 次, ∗ * 等价于 0 , {0,} 0
  • + + + 匹配至少 1 次,它等价于 1 , {1,} 1
  • ? ? ? 匹配 0 次或 1 次

神功小成

学习到这里,你已经掌握了不少正则表达式规则了,但是不能骄傲自满,我们还要把一些容易埋坑的地方给踩掉。

回到我们最开始的判断电话号码的例子,如果我们规定电话号码不能以 0 开头,那么应该怎么写正则表达式呢?

很明显,如果用 d 11 \\d{11} d11 来匹配的话,0 是会进行匹配的,不满足我们的要求。这样的场景我们需要用 [ ] 来匹配, [ ] 用于匹配指定范围内的字符,比如 [ 123456789 ] [123456789] [123456789] 可以匹配 1-9。

聪明如你,看到这里应该就明白了,解决这个问题的正则表达式是

[123456789]\\d{10}

这里有一个小技巧, [ 123456789 ] [123456789] [123456789] 写起来太麻烦,可以写作 [ 1 − 9 ] [1-9] [19]。类似地, [ a − g ] [a-g] [ag] 表示 [ a b c d e f g ] [abcdefg] [abcdefg] [ U − Z ] [U-Z] [UZ] 表示 [ U V W X Y Z ] [UVWXYZ] [UVWXYZ]

如果既可以是数字 1-9,又可以是字母 a-g,还可以是字母 U-Z,我们可以这么写 [ 1 − 9 a − g U − Z ] [1-9a-gU-Z] [19agUZ]

System.out.println("3".matches("[1-9a-gU-Z]"));		//输出true
System.out.println("d".matches("[1-9a-gU-Z]"));		//输出true
System.out.println("Y".matches("[1-9a-gU-Z]"));		//输出true
System.out.println("A".matches("[1-9a-gU-Z]"));		//输出false

如果是 0-1,8-9 这样的组合,我们可以直接写为 [ 0189 ] [0189] [0189],这样非常简洁。我们也可以按照规则把它写为 [ 0 − 18 − 9 ] [0-18-9] [0189],因为正则一次只匹配一个字符,所以这样写不会产生歧义,计算机不会把它误解成要匹配 0-18 之类的。

System.out.println("9".matches("[0189]"));		//输出true
System.out.println("8".matches("[0-18-9]"));	//输出true
System.out.println("5".matches("[0-18-9]"));	//输出false

还有一种写法可以实现这一点,那就是“或”运算符。正则的“或”运算符是 ∣ | [ 0189 ] [0189] [0189] 也可以写成 [ 0 ∣ 1 ∣ 8 ∣ 9 ] [0|1|8|9] [0189]

System.out.println("1".matches("[0|1|8|9]"));	//输出true
System.out.println("5".matches("[0|1|8|9]"));	//输出false

“或”可以实现很多功能,它不局限于单个字符。关于“或”的妙用,多多上机练习慢慢就会发现了。

System.out.println("ab".matches("ab|AB"));	//输出true
System.out.println("AB".matches("ab|AB"));	//输出true
System.out.println("12".matches("ab|AB"));	//输出false

如果我们想排除某些字符,比如这个位置不能是 [ 123 ] [123] [123]。之前说的取反是针对字母的, [ ] [] []没有大写这一说, [ ] [] []取反的方式是 [^],比如不能是 [ 123 ] [123] [123] 的表示方法是 [^123] 或者 [^1-3]。

System.out.println("5".matches("[^1-3]"));	//输出true
System.out.println("2".matches("[^1-3]"));	//输出false

我们再介绍一个贪婪模式与非贪婪模式。贪婪模式在基础部分仅做了解,在进阶部分我们会用例子详细说明。

  • 贪婪就是尽量往多的匹配,比如"+",匹配一次或多次 ,优先匹配更多的。
  • 非贪婪模式就是尽量少的匹配,比如"?",匹配0次或1次,优先匹配少的。
  • 编程中如何区分呢,在贪婪匹配后面加个"?",就变成了非贪婪模式。

学习到了这一步,可以恭喜你完成了对新手的全部教程。你现在所学习的正则知识已经足够你应付许多应用场景了。

现在你可以多看看练功宝典,投入到实践的海洋中,多多练习,扎实基础。也可以选择继续深入地进行学习,掌握更高深的正则规则。正则表达式-进阶

练功宝典

常用的匹配规则

模式描述
\d匹配任意数字,等价于[0-9]
\D匹配任意非数字的字符
\w匹配字母、数字及下划线
\W匹配不是字母、数字及下划线的字符
\s匹配任意空白字符,等价于[\t\n\r\f]
\S匹配任意非空字符
\n匹配一个换行符
\t匹配一个制表符
^匹配一行字符串的开头
$匹配一行字符串的结尾
.匹配任意字符,不特殊指定话默认不匹配换行符
[…]用来表示一组字符,比如[ack]匹配a、c、k
[^…]不在[…]中的字符,比如[^abc]匹配除了a、b、c之外的字符
*匹配0个或多个表达式
+匹配1个或多个表达式
?匹配0个或1个前面的正则表达式定义的片段,非贪婪方式
{n}精确匹配n个前面的表达式
{n,m}匹配n到m次前面正则表达式定义的片段,贪婪方式
a|b匹配a或b
( )匹配括号内的表达式,也表示一个组

参考文章

LeetCode力扣——【正则表达式】王国奇遇记

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值