Skip to content

Latest commit

 

History

History
240 lines (153 loc) · 7.58 KB

4.2正则表达式.md

File metadata and controls

240 lines (153 loc) · 7.58 KB

正则表达式的简介

1.1. 正则表达式是什么

正则表达式(Regular Expression)是一个用正则符号写出的公式,程序对这个公式进行语法分析,建立一个语法分析树,再根据这个分析树结合正则表达式的引擎生成执行程序(这个执行程序我们把它称作状态机,也叫状态自动机),用于字符匹配。

1.2. 如何学习正则

正则表达式是一个强大的文本匹配工具,但是它的规则很复杂,理解起来较为困难,容易让人望而生畏。

刚接触正则时,我看了一堆正则的语义说明,但是仍然不明所以。后来,我多接触一些正则的应用实例,渐渐有了感觉,再结合语义说明,终有领悟。我觉得正则表达式和武侠修练武功差不多,应该先练招式,再练心法。如果一开始就直接看正则的规则,保证你会懵逼。当你熟悉基本招式(正则基本使用案例)后,也该修炼修炼心法(正则语法)了。真正的高手不能只靠死记硬背那么几招把式。就像张三丰教张无忌太极拳一样,领悟心法,融会贯通,少侠你就可以无招胜有招,成为传说中的绝世高手。

以上闲话可归纳为一句:学习正则应该从实例去理解规则。

元字符

元字符(metacharacters)就是正则表达式中具有特殊意义的专用字符

基本元字符

正则表达式的元字符难以记忆,很大程度上是因为有很多为了简化表达而出现的等价字符。而实际上最基本的元字符,并没有那么多。对于大部分的场景,基本元字符都可以搞定。

多选(|

【示例】匹配一个确定的字符串

checkMatches("abc", "abc");

如果要匹配一个确定的字符串,非常简单,如例 1 所示。但是,如果你不确定要匹配的字符串,希望有多个选择,怎么办?答案是:使用元字符| ,它的含义是或。

【示例】匹配多个可选的字符串

// 测试正则表达式字符:|
Assert.assertTrue(checkMatches("yes|no", "yes"));
Assert.assertTrue(checkMatches("yes|no", "no"));
Assert.assertFalse(checkMatches("yes|no", "right"));

// 输出
// yes	matches: yes|no
// no	matches: yes|no
// right	not matches: yes|no

分组(()

如果你希望表达式由多个子表达式组成,你可以使用 ()

【示例】匹配组合字符串

Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "ended"));
Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "ending"));
Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "playing"));
Assert.assertTrue(checkMatches("(play|end)(ing|ed)", "played"));

// 输出
// ended	matches: (play|end)(ing|ed)
// ending	matches: (play|end)(ing|ed)
// playing	matches: (play|end)(ing|ed)
// played	matches: (play|end)(ing|ed)

指定单字符有效范围([]

前面展示了如何匹配字符串,但是很多时候你需要精确的匹配一个字符,这时可以使用[]

【示例】字符在指定范围

// 测试正则表达式字符:[]
Assert.assertTrue(checkMatches("[abc]", "b"));  // 字符只能是a、b、c
Assert.assertTrue(checkMatches("[a-z]", "m")); // 字符只能是a - z
Assert.assertTrue(checkMatches("[A-Z]", "O")); // 字符只能是A - Z
Assert.assertTrue(checkMatches("[a-zA-Z]", "K")); // 字符只能是a - z和A - Z
Assert.assertTrue(checkMatches("[a-zA-Z]", "k"));
Assert.assertTrue(checkMatches("[0-9]", "5")); // 字符只能是0 - 9

// 输出
// b	matches: [abc]
// m	matches: [a-z]
// O	matches: [A-Z]
// K	matches: [a-zA-Z]
// k	matches: [a-zA-Z]
// 5	matches: [0-9]

指定单字符无效范围( [^]

【示例】字符不能在指定范围

如果需要匹配一个字符的逆操作,即字符不能在指定范围,可以使用[^]

// 测试正则表达式字符:[^]
Assert.assertFalse(checkMatches("[^abc]", "b")); // 字符不能是a、b、c
Assert.assertFalse(checkMatches("[^a-z]", "m")); // 字符不能是a - z
Assert.assertFalse(checkMatches("[^A-Z]", "O")); // 字符不能是A - Z
Assert.assertFalse(checkMatches("[^a-zA-Z]", "K")); // 字符不能是a - z和A - Z
Assert.assertFalse(checkMatches("[^a-zA-Z]", "k"));
Assert.assertFalse(checkMatches("[^0-9]", "5")); // 字符不能是0 - 9

// 输出
// b	not matches: [^abc]
// m	not matches: [^a-z]
// O	not matches: [^A-Z]
// K	not matches: [^a-zA-Z]
// k	not matches: [^a-zA-Z]
// 5	not matches: [^0-9]

限制字符数量({}

如果想要控制字符出现的次数,可以使用 {}

字符 描述
{n} n 是一个非负整数。匹配确定的 n 次。
{n,} n 是一个非负整数。至少匹配 n 次。
{n,m} m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。

【示例】限制字符出现次数

// {n}: n 是一个非负整数。匹配确定的 n 次。
checkMatches("ap{1}", "a");
checkMatches("ap{1}", "ap");
checkMatches("ap{1}", "app");
checkMatches("ap{1}", "apppppppppp");

// {n,}: n 是一个非负整数。至少匹配 n 次。
checkMatches("ap{1,}", "a");
checkMatches("ap{1,}", "ap");
checkMatches("ap{1,}", "app");
checkMatches("ap{1,}", "apppppppppp");

// {n,m}: m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。
checkMatches("ap{2,5}", "a");
checkMatches("ap{2,5}", "ap");
checkMatches("ap{2,5}", "app");
checkMatches("ap{2,5}", "apppppppppp");

// 输出
// a	not matches: ap{1}
// ap	matches: ap{1}
// app	not matches: ap{1}
// apppppppppp	not matches: ap{1}
// a	not matches: ap{1,}
// ap	matches: ap{1,}
// app	matches: ap{1,}
// apppppppppp	matches: ap{1,}
// a	not matches: ap{2,5}
// ap	not matches: ap{2,5}
// app	matches: ap{2,5}
// apppppppppp	not matches: ap{2,5}

转义字符(/

如果想要查找元字符本身,你需要使用转义符,使得正则引擎将其视作一个普通字符,而不是一个元字符去处理。

* 的转义字符:\*
+ 的转义字符:\+
? 的转义字符:\?
^ 的转义字符:\^
$ 的转义字符:\$
. 的转义字符:\.

如果是转义符 \ 本身,你需要使用 \\

指定表达式字符串的开始(^)和结尾($

如果希望匹配的字符串必须以特定字符串开头,可以使用 ^

注意:请特别留意,这里的 ^ 一定要和 [^] 中的 ^ 区分。

【示例】限制字符串头部

Assert.assertTrue(checkMatches("^app[a-z]{0,}", "apple")); // 字符串必须以app开头
Assert.assertFalse(checkMatches("^app[a-z]{0,}", "aplause"));

// 输出
// apple	matches: ^app[a-z]{0,}
// aplause	not matches: ^app[a-z]{0,}

如果希望匹配的字符串必须以特定字符串结尾,可以使用 $

【示例】限制字符串尾部

Assert.assertTrue(checkMatches("[a-z]{0,}ing$", "playing")); // 字符串必须以ing结尾
Assert.assertFalse(checkMatches("[a-z]{0,}ing$", "long"));

// 输出
// playing	matches: [a-z]{0,}ing$
// long	not matches: [a-z]{0,}ing$

等价字符

等价字符,顾名思义,就是对于基本元字符表达的一种简化(等价字符的功能都可以通过基本元字符来实现)。

  • 在没有掌握基本元字符之前,可以先不用理会,因为很容易把人绕晕。

  • 等价字符的好处在于简化了基本元字符的写法。