正则一直是我一大痛点,一直都想解决这个问题,但是奈何每次看到那么多符号就发蒙,所以就一直拖下去了。直到最近总是被别人问到如何在hql中使用rlike查询符合特定规则的字段,然后各种不会,结果被鄙视的一塌糊涂,无奈,这才静下心来慢慢研究。
以前看正则就是一堆符号,代表各个意思,如\d表示数字,\d+表示一个或者多个连续数字,单看每个规则都可以理解,除了组合,但是实际使用时真的很难组织到一起。归根结底还是因为对这些符号的理解不够深入。所以正则还是得多写,推荐一个在线练习的网站RegexGolf。好了,下面写写自己学习正则的一些总结吧,希望能够帮助到别人,也帮助自己总结总结。
正则的基础知识:
字面值
正则表达式由只代表自身的字面值和代表特定含义的元字符组成。 只代表自身的字面值指的是普通的字符,如abcde,特殊含义的元字符包括:字符 含义 \ 反斜线字符 \0n 带有八进制值 0 的字符 n (0 <= n <= 7) \0nn 带有八进制值 0 的字符 nn (0 <= n <= 7) \0mnn 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7) \xhh 带有十六进制值 0x 的字符 hh \uhhhh 带有十六进制值 0x 的字符 hhhh \t 制表符 (‘\u0009’) \n 新行(换行)符 (‘\u000A’) \r 回车符 (‘\u000D’) \f 换页符 (‘\u000C’) \a 报警 (bell) 符 (‘\u0007’) \e 转义符 (‘\u001B’) \cx 对应于 x 的控制符 字符类
字符类是字符在方括号中的集合。表示“找到集合里任意一个字符“。例如:字符 含义 [abc] a、b 或 c(简单类) [^abc] 任何字符,除了 a、b 或 c(否定) [a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围) [a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集) [a-z&&[def]] d、e 或 f(交集) [a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去) [a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去) 从上表可以看出[]里面可进行并集/交集/差集操作。对于字符范围是根据ASCII值的大小来的,例如[A-z]也是可以的,甚至能够匹配[,但是完全不建议如此使用,推荐使用的字符范围:[0-9]/[a-z]/[A-Z]。
字符类还有一些预定义的字符类:字符 含义 . 任何字符(与行结束符可能匹配也可能不匹配) \d 数字:[0-9] \D 非数字: [^0-9] \s 空白字符:[ \t\n\x0B\f\r] \S 非空白字符:[^\s] \w 单词字符:[a-zA-Z_0-9] \W 非单词字符:[^\w] 使用上面的预定义字符类能够更加方便的表示字符范围。需要牢记。
注意: 区间是字符的区间,不是数字的区间。正则表达式[1-31]表示“找到一个1或一个2或一个3”,不是“找到一个从1到31的整数”。乘法器
字符 含义 X? X,一次或一次也没有 X* X,零次或多次 X+ X,一次或多次 X{n} X,恰好 n 次 X{n,} X,至少 n 次 X{n,m} X,至少 n 次,但是不超过 m 次 X可以使一个普通字面值,如a+:一个或多个a,也可以是一个字符类,如[abc]{2},表示a/b/c后跟a/b/c。
值得注意的是优先选择更长的匹配,因为乘法器是贪婪的。如果你输入的文本是I had an aaaaawful day,该正则表达式就会在aaaaawful中匹配到aaaaa。不会在第三个a后就停止匹配。
乘法器是贪婪的,但它不会忽略一个更好的匹配。如果你的输入文本为I had an aaawful daaaaay,之后这个正则表达式会在第一次的匹配中于aaawful找到aaa。只有在你说“给我找到另一个匹配”的时候,它才会继续搜索然后在daaaaay中找到aaaaa。
惰性:
正则表达式“.”表示“找到一个双引号,接着找到尽可能多的字符,最后再找到一个双引号”。注意一下被.匹配的内部字符,很可能包含多个双引号。这通常不是非常有用。乘法器可通过追加问号(?)来实现惰性。“.?”*表示“匹配一个双引号,跟着一个尽可能少的字符,再跟着一个双引号”。分支
可以使用管道符号来实现匹配多种选择。字符 含义 X|Y X或者Y 组合
可以使用圆括号来组合表达式。例:
在一周中找到一天,使用(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day,这里如果把小括号或者中括号,结果是完全不一样的,因为中括号是字符类,即里面的Mon并不是完全匹配Mon,而是只要匹配M/o/n其中一个即可。
同时组合后面还可跟上乘法器,如:\w+(\s+\w+)*代表“找到一个或多个单词,它们以空格隔开”。边界
边界分成:单词边界,行边界,文本边界- 单词边界
单词边界是一个单词字符和非单词字符之间的位置。记住,一个单词字符是\w,它是[0-9A-Za-z_],一个非单词字符是\W,也就是[^0-9A-Za-z_]。
文本的开头和结尾总是当作单词边界。
输入的文本it’s a cat有八个单词边界,分别为:文本开头-i,t-‘,’-s,s-空格,空格-a,a-空格,空格-c,t-文本结尾。 - 行边界
每一块文本会分解成一个或多个行,用换行符分隔。
注意文本不是以换行符结束,而是以行结束。然而,任何行,包括最后一行,可以包含零个字符。
起始行位置是在一个换行符和下一行的第一个字符之间。与单词边界一样,在文本的开头也算作一个起始的行。结束行位置是在行的最后一个字符和换行符之间。与单词边界一样,文本结束也算作行结束。 - 文本边界
很多实现提供一个标记,通过改变它来改变^和$的含义。从“行开始”和“行结束”变成“文本开始”和“文本结束”。其它的一些实现提供单独的元字符\A和\z来达到这个目的。
一些表示边界的符号:
字符 含义 ^ 行的开头 $ 行的结尾 \b 单词边界 \B 非单词边界 \A 输入的开头 \G 上一个匹配的结尾 \Z 输入的结尾,仅用于最后的结束符(如果有的话) \z 输入的结尾 其中^$是最常用的两个边界分隔符。
- 单词边界
捕获和替换:
捕获组
()在正则中被用来表示组,同时也可以用来捕获匹配上的子串,可以拥有多个捕获组,它们甚至可以嵌套使用,捕获组从左到右进行编号,只要计算左圆括号。例如:
对于表达式:(\w+) had a ((\w+) \w+),文本是I had a nice day,那么- 捕获组1是I。
- 捕获组2是nice day。
- 捕获组3是nice。
- 捕获组0是I had a nice day(根据具体实现不同)
如果表达式使用了两个捕获组,但是只捕获到一组,那么组2是空字符串。引用捕获组使用+组序号,如\1表示引用第一个捕获组。
- 后向引用
可以在同样的表达式中引用同一个捕获组,这称为后向引用。
例:表达式[abc]{2}表示“匹配aa或ab或ac or ba或bb或bc或ca或cb或cc”,但是表达式([abc])\1表示“匹配aa或bb或cc”。
以上就是正则的全部知识,其实了解正则的知识点很简单,但是真要应用到实际中还是需要通过大量的练习才能做到熟练使用。
实际案例
工具:Notepad++ 查找:([{;])\s+ 替换:\1
以上案例会不断更新,已记录一些自己对正则使用的经历。
参考文章
55分钟学会正则表达式(译) JDK API文档