相关文章推荐
体贴的蘑菇  ·  Job | Kubernetes·  1 年前    · 
文质彬彬的海豚  ·  Quarter-BPS states, ...·  1 年前    · 
玩足球的打火机  ·  javascript - Mapbox ...·  1 年前    · 

Regex正则表达式

正则表达式( regular expression )描述了一种字符串匹配的模式( pattern ),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

正则表达式 匹配的字符串
k k
abc abc
[abc] [...]表示字符集,是其中一个即可:a/b/c
[abc][123] a1/a2/a3/b1/b2/b3/c1/c2/c3
[a-z] a/b/c/d...z
[a-zA-Z0-9] a/A/z/Z/0/9..
[^a-zA-Z] ^表示排除一个范围:排除英文字母
[\u4e00-\u9fa5] 中文范围
\d 数字[0-9]
\D 排除数字[^0-9]
\w 单词字符和下划线[a-zA-Z_0-9]
\W 排除单词字符和下划线[^a-zA-Z_0-9]
\s 空白字符:回车/换行/制表符/......
\S 排除空白字符
. 任意字符
[abc]? ?表示0个或1个
[abc]?[123] 1/2/3/a1/a2...
[abc]* *表示0到多个
[abc]*[123] 1/2/3/a1/aaabccba1/...
[abc]+ +表示1到多个
[abc]+[123] a1/ab2/abcbcac1/...
[abc]{3} {}大括号表示固定数量:aaa/bca/cbc/...
[abc]{3,5} 3-5个:acc/abca/abcab/...
[abc]{3,} 3到多个:abc/abcccccaaaa/...
| 或,匹配左右其中一个表达式即可

举例说明:
限定符有 * + ? {n} {n,} {n,m} 共6种。
zo+ ,可以匹配 zo zooo 等,但不能匹配 zozo ,+ 号代表前面的字符必须至少出现一次(1次或多次)。如果想匹配 zozo ,需要把 zo 做为一个整体写正则表达式 (zo)+

出现在范围表达式之后,它应用于前边单个的范围表达式,例如匹配 1~99 的正整数表达式: [1-9][0-9]? 。[1-9] 设置第一个数字不是 0,[0-9]? 表示 0-9 不出现,或者出现一次。

matches(正则表达式)

判断当前字符串,是否匹配正则表达式

栗子:匹配身份证号码
身份证号可以为15位(一代身份证)或者18位,18位的最后一位为校验码可为0-9或者x。

public class Test {
	public static void main(String[] args) {  
		System.out.println("输入身份证号:");
		String s = new Scanner(System.in).nextLine();
		 * 123456789012345
		 * 123456789012345678
		 * 12345678901234567x
		 * 12345678901234567X
		 * \d{15}|\d{17}[\dxX]
		 * 需要对“\”进行转义
		 * \->\\
		String regex = "\\d{15}|\\d{17}[\\dxX]";
		if(s.matches(regex)){
			System.out.println("格式正确");
		}else{
			System.out.println("格式错误");

运行结果:

栗子:匹配固定电话

这里仅为练习,假设固定电话的格式有以下几种

public class Test {
	public static void main(String[] args) {  
		System.out.println("输入固定电话:");
		String s = new Scanner(System.in).nextLine();
		 * 123456
		 * 1234567
		 * 12345678
		 * (010)12345678
		 * (0102)12345678
		 * 010-123456
		 * 0102-1234567
		 * (\\d{3,4}-|\\(\\d{3,4}\\))?\\d{6,8}
		String regex = "(\\d{3,4}-|\\(\\d{3,4}\\))?\\d{6,8}";
		if(s.matches(regex)){
			System.out.println("格式正确");
		}else{
			System.out.println("格式错误");

replaceAll(正则表达式,子串)

将找到的匹配子串,替换为新的子串

String regex = "store";
String s = "http://store.store.com";
System.out.println(s.replaceAll(regex, "www"));
System.out.println(s);

运行结果,不会改变之前的字符串

http://www.www.com
http://store.store.com

replace 和 replaceAll 可以达到一样的效果,他们的区别是:

replace:参数为 target 和 replacement,也就是替换的目标对象和新对象

replaceAll:参数为 regex 和 replacement,第二个参数都一样,第一个参数表示正则表达式,也就是说 replaceAll 可以支持正则表达替换。例如:

String str = "www.google.com";
System.out.print("匹配成功返回值 :" );
System.out.println(str.replaceAll("(.*)google(.*)", "baidu" ));

运行结果:

匹配成功返回值 :baidu

replaceFirst:参数和 replaceAll一致,唯一不同的是它执行匹配第一个结果

split(正则表达式)

用匹配的子串,拆分字符串

public class Test {
	public static void main(String[] args) {  
		System.out.println("输入关键词列表,用逗号、分号、空格分隔");
		String s = new Scanner(System.in).nextLine();
		String regex = "[,; ]+";
		String[] a = s.split(regex);
		for(int i=0;i<a.length;i++){
			System.out.println(a[i]);

java.util.regex.Pattern 和 java.util.regex.Matcher

Pattern 封装正则表达式
Matcher 封装正则表达式,和要匹配的字符串

Pattern p = Pattern.compile(正则表达式);
Matcher m = p.matcher(要匹配的字符串);

find()
向后查找下一段匹配的子串。返回 boolean 值表示是否找到

find(int from)
从指定位置向后查找

group()
提取刚刚找到的子串

start()、end()
刚刚找到的子串的起始位置和结束位置

栗子:匹配字符串中的3到多个数字

		System.out.println("输入:");
        String s = new Scanner(System.in).nextLine();
        //3到多个连续数字
        String regex = "\\d{3,}";
        Matcher m = Pattern.compile(regex)
                .matcher(s);
        //一直向后查找,直到false
        while (m.find()) {
            String s2 = m.group();
            int start = m.start();
            int end = m.end();
            System.out.println(start + "-" + end + ":" + s2);
abcd1234efg56higk789
4-8:1234
17-20:789

关于贪婪和非贪婪

*+限定符都是贪婪的,因为它们会尽可能多的匹配文字,只有在它们的后面加上一个?就可以实现非贪婪或最小匹配。

例如当使用<.*>

String s = "<H1>Chapter 1 - 介绍正则表达式</H1>";
String regex = "<.*>";
Matcher m = Pattern.compile(regex).matcher(s);
while (m.find()) {
		String s2 = m.group();
		System.out.println(s2);

匹配的结果是

<H1>Chapter 1 - 介绍正则表达式</H1>

如果用<.*?>,匹配的结果是

如果只想匹配开始的 H1 标签,表达式则是<\\w+?>,匹配结果是

通过在 *+? 限定符之后放置 ?,该表达式从"贪心"表达式转换为"非贪心"表达式或者最小匹配。

再举个例子可能更容易理解:
源字符串:aa<div>test1</div>bb<div>test2</div>cc
正则表达式一:<div>.*</div>
匹配结果一:<div>test1</div>bb<div>test2</div>

正则表达式二:<div>.*?</div>
匹配结果二:<div>test1</div><div>test2</div>

仅从应用角度分析,可以这样认为,贪婪模式,就是在整个表达式匹配成功的前提下,尽可能多的匹配,也就是所谓的“贪婪”。非贪婪模式,就是在整个表达式匹配成功的前提下,尽可能少的匹配,也就是所谓的“非贪婪”。

定位符 ^ $ \b \B

定位符简单来说就是限定某些字符出现的位置。

正则表达式的定位符有

字符描述
^匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配。
$匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配。
\b匹配一个单词边界,即字与空格间的位置。
\B非单词边界匹配。
例如正则表达式^a
由于使用了^定位符,因此字符串中必须以a开头。所以匹配上面正则表达式的字符有 abc、absolute 等以 a 开头的字符串,但是 back、123 等就不匹配了。

^后直接跟字符串时,表示以后面的整个字符串开始。当后面是一个表达式时,表示匹配以该表达式开始的字符串。下面举例。

^123[0-9]*[3]: 查找以【123 + 任意多的0-9之间的字符 + 3】 开始的字符串。可匹配1239991231235673等。
^[123][0-9]*[3]:查找以【1或2或3】开始 + 0-9之间的数字任意多个数字 + 3 的字符串。可以匹配 29993303等。
^(123).*[3]:查找以【123】开始 + 任意多个任意字符 + 3 的字符串。可以匹配 123aabbcc31233等。

^a[^a]的区别
上尖号的使用有2种情况:(1)定位符;(2)[^…]元字符

很多初学者容易混淆上尖号,其实大家可以这样理解:上尖号,只有一种特殊情况,就是[^…]这种元字符的时候,上尖号才表示“非”,其他情况,上尖号都是表示定位符。

在正则表达式中,使用$定位符来限定结尾位置的字符。
例如正则表达式a$
由于使用了$定位符,因此字符串中必须以 a 结尾。所以匹配上面正则表达式的字符串有 panda、nana 等以 a 结尾的字符串,但是 abc、helicopter 等就不匹配了。

$前面直接跟字符时,表示匹配以前面整个字符串结束的字符串, 当前面是一个表达式时,表示匹配以该表达式结束的字符串。
[0-9]+123 : 匹 配 前 边 1 到 多 个 数 字 , 最 后 以 【 123 】 结 尾 的 字 符 串 , 如 ‘ 002244123 ‘ 等 。 ‘ [ 456 ] [ 123 ] :匹配前边 1 到多个数字,最后以 【123】 结尾的字符串,如`002244123`等。 `^[456][123] :匹配前边1到多个数字,最后以【123】结尾的字符串,如‘002244123‘等。‘[456][123]:查找以【4或5或6】开始,以【1或2或3】结束的字符串,如43`等。

\b包含了字与空格间的位置
正则表达式er\\b,匹配order to中的er,但不匹配 verb 中的 er。

\b也包含了目标字符串的开始和结束位置。
正则表达式\\ba[a-z]{7}\\b
匹配以字母“a”开头的长度等于8的任意单词。因此\b限定了单词的开头和结尾。

使用2个\b来匹配一个单词,这是非常常用的方法。如果大家以后见到正则表达式中有2个\b,也应该知道这是匹配单词的。

\b 字符的位置是非常重要的。如果它位于要匹配的字符串的开始,它在单词的开始处查找匹配项。如果它位于字符串的结尾,它在单词的结尾处查找匹配项例如:
正则表达式ter\\b匹配单词 Chapter 中的字符串 ter,因为它出现在单词边界的前面。

在正则表达式中,使用\B定位符来限定一个非单词开始或结束时的字符。
正则表达式er\\B,匹配 verb 中的 er,但不匹配 order 中的 er 。

正则表达式中的替换

		String str = "2013hello04world20";
        //将数字替换成*
        System.out.println(str.replaceAll("\\d", "*"));
        //将连续数字换成*
        System.out.println(str.replaceAll("\\d+", "*"));
        //将手机后四位替换成*
        str = "15200001111";
        System.out.println(str.replaceAll("\\d{4}$", "****"));
        //将手机中间四位替换成*
        System.out.println(str.replaceAll("(\\d{3})(\\d{4})(\\d{3})", "$1****$3"));
        //给链接地址增加a标签转换成超链接
        str = "http://www.baidu.com,http://www.google.com";
        System.out.println(str.replaceAll("(http://www\\..*?\\.com)", "<a href='$1'>$1</a>"));
****hello**world**
*hello*world*
1520000****
152****1111
<a href='http://www.baidu.com'>http://www.baidu.com</a>,<a href='http://www.google.com'>http://www.google.com</a>

需要说明的是,$1,$2等分别对应的是一个小括号

编辑器中使用正则替换

我们使用 Editplus 编辑一些文字,需要将 “我是程序员啊” 中的 程序员 替换成 工程师,当然直接匹配可以,复杂一些可以使用正则
Ctrl+H,打开替换页面
其中 $1 和 $2 分别代表第 1 个和第 2 个括号内匹配到的内容,点击 replaceAll
替换指定内容到行尾

每次遇到 abc 将 abc 后边内容改为 def

将连续的数字加上中括号
删除每一行行尾的指定字符

删除每行末尾的 345
替换带有半角括号的多行
用以下正则表达式

<script LANGUAGE=JavaScript1.1>\n<!--\nhtmlAdWH.'93163607','728','90'.;\n//-->\n</SCRIPT>\n

由于“(”、“)”被用做预设表达式(或者可以称作子表达式)的标志,所以可以把“(”、“)”使用任意字符标记替代,即半角句号:“.”

转义是怎么回事

需求:格式化金额。由于后台金额格式可以变化,所以由后台返回金额格式。app 端需替换服务器返回的格式化字符串中的"{0}"为金额。例如后台返回 ¥{0},我们的金额格式就为 ¥123。后台返回 ${0},我们的金额格式就为 $123

		String unformattedMoney = "12.00";
        String s = "${0}";
        String regex = "\\{0\\}";
        s = s.replaceAll(regex, unformattedMoney);
        System.out.println(s);
$12.00

正则表达式中{}这样的字符有特殊的意义,最开始的表格显示 [abc]{3} 中大括号表示固定数量,这个表达式可匹配:aaa/bca/cbc/…

而这里我们需要匹配大括号时,就需要转移写成 \{。但是\本身也是具有特殊意义的转义字符,所以\就需要写成\\{。需要先对\进行一次转义。

所谓特殊字符,就是一些有特殊含义的字符,例如roo*t 中的*,正常情况下这个正则表达式可以匹配 root、roooot、roooooot 等,*号代表字符出现 0 到多次。但是如果要查找字符串中的*符号,则需要对*进行转义,即在其前加一个\ro\\**ot 匹配 ro****ot、root 等。

许多元字符要求在试图匹配它们时特别对待。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符\放在它们前面。下表列出了正则表达式中的特殊字符

特别字符描述
$匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 也匹配 '\n' 或 '\r'。要匹配 字符本身,请使用 \$。
( )标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。
*匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。
+匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。
.匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。
[标记一个中括号表达式的开始。要匹配 [,请使用 \[。
?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。
\将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\' 匹配 "\",而 '\(' 则匹配 "("。
匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。
{标记限定符表达式的开始。要匹配 {,请使用 \{。
|指明两项之间的一个选择。要匹配 |,请使用 \

一些符号组合到底什么意思

?:非获取匹配,匹配冒号后的内容,但不获取匹配结果,不进行存储供以后使用

如果想匹配 “program” 和 “project” 这两个单词,正则表达式可表示为 program|project,也可表示为 pro(gram|ject)。但用了()就表示会匹配括号里存在的内容且存储一份(看上面的替换的栗子就知道),用 | 隔开了,也就是说 gram 和 ject 都被存储了一份 但这样存储的内容是无意义的,所以表达式写成这样 pro(?:gram|ject),一是显得比较简洁,二是不会存储无意义的内容。

下面说明存储问题:
如果需要匹配连续重复的单词,如 lost lost 这里发现 lost 重复了,可用正则来 \\b(\\w+)\\b\\s+\\1\b 来找这样连续重复单词。
\b 匹配单词的开始
(\w+) 匹配单词并存储一份单词,当后面有反向引用时,则可以调用这个存储的单词
\b 匹配单词的结束
\s+一个或多个空格
\1这个是反向引用,引用前面括号里存储的单词 也就是 \w+
\b单词结束
这时如果把?:加进去,这个表达式就无效了。因为 (?:\w) 这个单词虽可以被匹配但不会存储一份,后面出现的 \1 也不会调用前面括号里的单词,所以表达式就失效了。

www.runoob.com/regexp/rege…

分类:
代码人生
标签: