正则表达式

基础

  • 需要谨记的3个字母

\d

匹配数字

\w

匹配字母、数字、下划线

\s

匹配空白符(例如空格、换行符、制表符等)

大写表示匹配其相反的东西,如:\D 匹配非数字字符,\S匹配非空白字符

 

  • 常用的匹配字符

[0-9]

匹配在此区间的数字,同\d

[a-zA-Z]

匹配在此区间的字母,可以多个区间一起

[x.|9\\]

匹配在中括号内的字符,里面的都是普通字符没其他含义,除斜杠比较特殊需转义

[^a8]

匹配除中括号内字符之外的字符,和上面3个字母大写匹配其相反的东西一样的道理

hello

匹配常规字符串

^hello

匹配行首的hello

hello$

匹配行末的hello

hello|world

匹配hello或者world(正则中没有与运算)

.

匹配除换行符(\n、\r)之外的任意字符

[\u4e00-\u9fa5]

匹配中文字符

[\s\S]

匹配任意字符(随便两个互补区间都可以表示任意字符)

 

  • 表示字符数量的描述符(描述前面子表达式的匹配数量)

*

零次或多次

+

一次或多次

?

零次或一次

{n}

整数n

{n,m}

n~m次,包含nm

{n,}

n次以上,包含n

默认是描述前面一个字符,如果是多个字符的子表达式,需要括号括起来。

如:(pattern)*     th(is|at)       括号别有用处,后面会讲到

 

进阶

  • 获取匹配与非获取匹配

(pattern)

 

这里用括号将匹配子表达式括起来,从匹配结果来看加括号不影响正常匹配。

加括号主要是为了获取该子表达式匹配到的内容

获取匹配,怎么用我们后面会讲到

(?:pattern)

如果我们加括号只是为了圈定一串子表达式,而不用获取匹配到的内容。

上面的例子我们最好这样  th(?:is|at),等同于this|that

非获取匹配

hello(?=world)

如果一字符串包含多处hello,只匹配后面跟着worldhello,匹配结果不包含world

例如,"Windows(?=95|2000)"能匹配"Windows2000"中的"Windows",但不能匹配"Windows98"中的"Windows"

非获取匹配

hello(?!world)

同理,只匹配后面没跟着worldhello,匹配结果不包含world

非获取匹配

(?<=hello)world

如果一字符串包含多处world,只匹配前面跟着helloworld,匹配结果不包含hello

例如,"(?<=95|2000)Windows"能匹配"2000Windows"中的"Windows",但不能匹配"98Windows"中的"Windows"

非获取匹配

(?<!hello)world

同理,只匹配前面没跟着helloworld,匹配结果不包含hello

非获取匹配

 

  • 非贪婪匹配

举个例子:"hello world and hi world"  我们需要匹配其中的"hello world""hi world"

  1. 我们可以这样子h.*world,但是我们运行后发现匹配到的结果是"hello world and hi world",这结果也没毛病,确实符合我们写出来的正则表达式。
  2. 如何得到我们想要的结果呢,我们这样子h.*?world,就能匹配到"hello world""hi world"

贪婪匹配:尽可能匹配最长的字符串

非贪婪匹配:尽可能匹配最短的字符串

个人对非贪婪匹配的理解是:从左往右匹配到符合正则的最短结果,返回结果并继续往后匹配下一结果。

 

  • 扩展(思考)

匹配typepasswordinput标签: <input type=radio name=xxx><input type=password name=yyy><input type=txt name=zzz>

  1. <input.*password.*>  匹配得到  <input type=radio name=xxx><input type=password name=yyy><input type=txt name=zzz>
  2. <input.*password.*?>  匹配得到  <input type=radio name=xxx><input type=password name=yyy><input type=txt name=zzz>
  1. <input((?!input).)*password.*?>匹配得到<input type=radio name=xxx><input type=password name=yyy><input type=txt name=zzz>

第一步写出匹配的轮廓,第二步使用非贪婪匹配排除掉后面部分,第三步inputpassword之间的字符串排除input

 

个人对((?!input).)*的理解:

  1. (?!input)i 匹配所有位置的‘i’,但不包括input开头的‘i’,即后面跟着nputi都不匹配。
  2. (?!input).  匹配任意字符(点的作用),但不包括input开头的‘i’。
  3. ((?!input).)*  匹配零个或多个(2)中的字符,既然input开头的‘i’被排除了,那么也就不存在input字符串了。

 

应用

java演示group一些特殊用法

  • 获取匹配

public static void main(String[] args) {

String str = "Her name is John, Her age is 18";

Pattern pattern = Pattern.compile("Her name is (\\w+), Her age is (\\d+)");

Matcher matcher  = pattern.matcher(str);

matcher.groupCount(); // 正则表达式中"获取匹配"的数量

while (matcher.find()) {

System.out.println(matcher.group());   // 输出整条正则表达式匹配到的内容

System.out.println(matcher.group(1) + " " + matcher.group(2));  // 输出括号中子表达式匹配到的内容

}

}

结果:

Her name is John, Her age is 18

John 18

 

  • 给group取别名,通过别名获取匹配(不影响表达式的正则匹配)Java 7+开始支持

Pattern pattern = Pattern.compile("Her name is (?<name>\\w+), Her age is (\\d+)");

matcher.group(name);

 

  • 在正则表达式中引用groupName匹配到值,\\k<groupName>表示groupName表达式匹配到的值

Pattern pattern = Pattern.compile("Hisname is (?<name>\\w+), Her name is (\\k<name>)");

如,匹配他和她名字相同的字符串:His name is John, Her name is John

 

  • 在替换函数中引用groupName匹配到的值,${groupName}

"His name is John".replaceAll("(?<name>\\w+)", "[${name}]");

结果:[His] [name] [is] [John]

 

linux命令grep使用正则

  • 我们知道grep命令的 -E 参数是将样式延伸的正则表达式来使用,但是它并不支持(?=pattern)和groupName这种用法。

这时我们使用 -P 代替之,表示让grep使用perl的正则表达式语法,因为perl的正则更加多元化,能实现更加复杂的场景

echo ==HelloWorld== | grep "Hello(?=World)" -P    

输出:==HelloWorld==

 

  • 拓展一下,因为grep命令匹配到一段就会整行输出,-o可以只输出匹配到的部分

echo ==HelloWorld== | grep "Hello(?=World)" -Po

输出:Hello

 

  • 再拓展一下,grep命令默认不支持跨行匹配,-z将输入内容作为行的合集,每个集合以空字节作为结束

我理解-z是将多行的输入内容看作一行,其中换行符也在一行当中,当作普通字符。

echo -e "==Hello\nWorld==" | grep "H[\s\S]*d" -Pz

输出:==Hello

 World==

 

python使用正则

 

 

练习/训练

http://tool.chinaz.com/regex/

 

posted @ 2020-04-08 01:40  zhangzongjian  阅读(253)  评论(0编辑  收藏  举报