正则表达式(RegularExpression),常用于对大段文本的处理,常用操作有:匹配、提取、替换。
第一个例子
比如有一个电话号码:13132314693
这个规则可以描述为:
- 以1开头
- 共11位数字
- 必须为0-9的数字
用正则表达式来描述可以写成:1[0-9]{10}
其中:1表示1开头,[0-9]表示(0到9)的数,{10}表示出现10次。合起来就是,1开头,0-9的数字出现10次。
重复次数匹配{}
比如有以下3个字符串:
- Huawei
- Huaawei
- Huaaawei
这3个字符串可以用正则描述为:Hua{1,3}wei
其中:{1,3}表示仅挨着的字符‘a’重复出现1次到3次,最少1次,最多3次,都是能够匹配得上的。
其他用法
扩展用法:
- {m,n} :表示出现最少m次,最多n次
- {m,} :表示最少m次,最多不限量
- {,n} :表示最少不限量,最多n次
- {m} :表示刚好出现m次
还有几个关键字符:
- * :表示0次,1次,或者N次,相当于{0,}
- + :表示1次,或N次,相当于{1,}
- ? :表示0次,或1次,相当于{0,1}
例如:Hua+wei 相当于 Hua{1,}wei
简单使用-匹配:JAVA代码如下
package com.example.editor.controller;
import java.util.regex.Pattern;
public class Test3
{
public static void main(String[] args)
{
String content = "Huawei";
String regex = "Hua+wei"; // “Hua*wei” / ”Hua?wei”
// 传入:正则式,要匹配的字符串
boolean isMatch = Pattern.matches(regex, content);
if(isMatch) {
System.out.println("匹配");
}
else {
System.out.println("不匹配");
}
}
}
字符区间匹配[]
有以下字符串:
- 梁瑞博
- 梁小博
- 梁微博
用正则表达式来描述可以写成:梁[瑞小微]博
其中:中括号[]表示区间内任意一个字符,梁表示以梁字开头,博表示以博字结尾,中间表示选项,选项只能匹配一个,如:梁[瑞小]博 就是不匹配的了。
其他用法
区间匹配[]的其他用法:
- [a-zA-Z] :所有的字母,大写+小写
- [0-9] :0-9的数字
- [0-9a-zA-Z_] :数字+小写字母+大写字母+下划线
- [^abc] :除abc三个字母以外都匹配,括号里面的以^开头,表示"非"。
- [0-3a-fA-F] :表示0,1,2,3,a,b,c,d,e,f,A,B,C,D,E,F
- [0-3abcdefABCDEF] :表示意义同 [0-3a-fA-F]
小练习
一般来说,小括号[]跟大括号{}是连用的,例如,要求用户设定一个6-12位的密码,密码可以是数字、字母、下划线。
用正则表达式描述为:[0-9a-zA-Z_]{6,12}
简单应用-匹配:JAVA代码如下
public static boolean checkInput(String passWord)
{
// 用户注册的时候,检查用户设置的密码是不是符合规则
// 假设我们规定要求用户设定一个6到12位数的密码,密码可以是数字、字母、下划线
String regex = "[0-9a-zA-Z_]{6,12}";
return Pattern.matches(regex, passWord);
}
分组()
有以下字符串:
- ChinaGreat
- ChinanaGreat
- ChinananaGreat
用正则表示式可以表示为:Chi(na)+Great
其中:'+'号我们上文说过,表示出现1次到N次,也可以写成:Chi(na){1,}Great ,其中的“(na)”表示na这两个字符作为一个组出现,是一体的。
字符的转义
例如我们需要表示完整的手机号,例如
- +86-13244534545
- +86-13245453456
- +86-13287657674
上面的三个手机号中,+86表示区号,但是在正则表达式看来,+,-都是关键字,并不是普通字符,那我们要怎么来描述如上的三个手机号字符串呢?
常规思想:+86-1[0-9]{10},表示"+86-1"开头,0-9的数字有10位。
但是我们发现在程序执行中报错了,原因是+号表示匹配前面紧挨着的字符0-N次,但是前面并没有字符,所以这样子是不行的。
在正则表达式中,如果想要把特殊字符作为普通字符串使用,需要用"\"进行转义。
例如:\+86-1[0-9]{10} : +号这里是作为普通字符使用,而不是关键字,所以用“\+”代替“+”。
写成JAVA代码则为:\\+86-1[0-9]{10} : 注意,此处有两个"\",代表此处有两层转义,一层是正则表达式对特殊字符的转义,另一层是JAVA代码对字符串的转义。
需要转义的字符
正则表达式需要转义的字符:
* + ? {} [] () . ^ $ \
常见的特殊字符
- . :表示任意一个字符
- \d :表示数字,相当于[0-9]
- \D :表示非数字,相当于[^0-9]
- \s :表示空白字符,相当于[\t\n\x0B\f\r]
- \S :表示非空白字符,相当于[^\s]
- \w :表示字符,相当于[a-zA-Z0-9_]
- \W :表示非字符,相当于[^\w]
- \b :匹配单词的开始或结束的位置
- \B :匹配不是单词的开始或结束的位置
- $ :匹配字符串的结束
- | :表示分支条件, 0\d{2}-\d{8}|0\d{3}-\d{7} :这个表达式能匹配两种以连字号分隔的电话号码:第一种是三位区号,8位本地号的号码(如010-12387456),第二种是四位区号,7位本地号的号码(如0376-2255435)。
例如:
- 1[0-9]{10} 与 1\d{10} 的效果是一样的
- <meta charset='UTF-8'> 可以描述为 <meta\s+charset=.*?>
- 2249508163lonng@qq.com 描述为 [\w@.]+ , 在JAVA中表示为:String regex = "[\\w@.]+" ; 注意,中括号里面的'.'没有歧义,无需转义。
实际应用
匹配
看上面的“重复次数匹配”部分
提取
根据匹配到的内容来提取
简单提取
应用示例-提取-简单提取:JAVA代码如下
public class Test2
{
public static void main(String[] args)
{
/**
使用正则表达式的提取,比如,在HTML中的图片标签格式为
<img src=’url’>
现在给出一段HTML文本,要求从中提取出所有<img>标签的内容
*/
// 准备文本
String content = "更多图片<img src='1.jpg'> <br>"
+ "示例2 <img src='2.jpg'> <br>"
+ "示例3 <img src='3.jpg'> <br>嘻嘻嘻";
// 正则表达式
String regex = "<img.*>"; // <img.*?> 这里要加一个?号,表示非贪心模式,+和*为贪心,?为非贪心
// 传入一个正则表达式,得到一个pattern对象
Pattern pattern = Pattern.compile(regex);
// 调用matcher方法传入文本得到matcher对象
Matcher m = pattern.matcher(content);
// m.find()方法如果匹配到了就返回true
while (m.find())
{
System.out.println("**匹配:" + m.group(0)); // group(0)指全部
}
/*
贪心跟非贪心的区别,因为<img.*>中.表示任意字符,*表示任意个字符,所以贪心会找到最后一个">"才算完。
<img.*?>输出:
**匹配:<img src='1.jpg'>
**匹配:<img src='2.jpg'>
**匹配:<img src='3.jpg'>
*/
/*
<img.*>输出:
**匹配:<img src='1.jpg'> <br>示例2 <img src='2.jpg'> <br>示例3 <img src='3.jpg'> <br>
*/
}
}
分组提取
应用示例-提取-分组提取1:JAVA代码如下
public class Test
{
public static void main(String[] args)
{
String content = " <img src=\"/image/16056333394802.png\" width=\"100\" />"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n" ;
// 提取<img>标签中的url参数
List<String> imaList = getImgStrTest(content);
System.out.println(imaList.toString());
System.out.println("Exit");
}
// <img>标签中的url参数提取
public static List<String> getImgStrTest(String htmlStr)
{
List<String> list = new ArrayList<>();
String regex = "<img.*?src=['\"]{1}/(.*?)['\"]{1}.*?>";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(htmlStr);
while (m.find())
{
// group(1)表示提取第一个分组里面的内容,即中(.*?)的内容,如果后面还有多个分组,那么就group(2)-3-4..
list.add(m.group(1));
}
return list;
}
}
程序输出:
[image/16056333394802.png, image/16056333479373.png, image/16056333479373.png, image/16056333479373.png]
应用示例-提取-分组提取2:JAVA代码如下
public class Test
{
public static void main(String[] args)
{
String content = " <img src=\"/image/16056333394802.png\" width=\"100\" />"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n" ;
List<String> imaList = getImgStr(content);
System.out.println(imaList.toString());
System.out.println("Exit");
}
public static List<String> getImgStr(String htmlStr)
{
List<String> list = new ArrayList<>();
String img = "";
// String regEx_img = "<img.*src=(.*?)[^>]*?>"; //图片链接地址
String regEx_img = "<img.*src\\s*=\\s*(.*?)[^>]*?>";
Pattern p_image = Pattern.compile(regEx_img, Pattern.CASE_INSENSITIVE); // 不分大小写匹配
Matcher m_image = p_image.matcher(htmlStr);
while (m_image.find())
{
// 得到<img />标签
img = m_image.group();
// 匹配<img>中的src数据
Matcher m = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)").matcher(img);
while (m.find()){
list.add(m.group(1));
}
}
return list;
}
}
程序输出:
[/image/16056333394802.png, /image/16056333479373.png, /image/16056333479373.png, /image/16056333479373.png]
替换
应用示例-替换-简单替换-把正则匹配到的所有字段全部统一替换
public class Test
{
public static void main(String[] args)
{
String content = "aaaabXaaabMaaaaaaaaabL";
String str = replaceImgTag1(content);
System.out.println(str);
System.out.println("Exit");
}
// 文本替换
public static String replaceImgTag1(String str)
{
String regex = "a*b";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(str);
// 用*号代替匹配的字符
return m.replaceAll("*");
}
}
程序输出:
*X*M*L
应用示例-替换-复杂替换-把正则匹配到的分开替换,各换各的
public class Test
{
public static void main(String[] args)
{
String content = "1:xxxxxx。"
+ "2:XXXXXXXXXXX。"
+ "3:yyyyyyyyyyy。"
+ "4";
List<String> strList = new ArrayList<String>();
strList.add("第一条");
strList.add("第二条");
strList.add("第三条");
strList.add("第四条");
String str = replace(content, strList);
System.out.println(str);
System.out.println("Exit");
}
public static String replace(String html, List<String> strList)
{
String regex = "\\d{1}";
Pattern pattern = Pattern.compile(regex);
Matcher m = pattern.matcher(html);
StringBuffer sb = new StringBuffer();
int i = 0;
while( m.find())
{
// 根据检索到上下文本,作相应的替换
m.appendReplacement(sb, strList.get(i));
i ++;
if(i == strList.size()) i = 0;
}
return sb.toString();
}
}
程序输出:
第一条:xxxxxx。第二条:XXXXXXXXXXX。第三条:yyyyyyyyyyy。第四条
应用示例-替换-按分组替换-把正则匹配到的分组里面的值替换
public class Test
{
public static void main(String[] args)
{
String content = " <img src=\"/image/16056333394802.png\" width=\"100\" />"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n"
+ "<img src=\"/image/16056333479373.png\" width=\"100\" />\r\n" ;;
List<String> strList = new ArrayList<String>();
strList.add("liangruibo/22222.png");
strList.add("liangruibo/33333.png");
strList.add("liangruibo/44444.png");
strList.add("liangruibo/55555.png");
String str = regReplaceImage(content, strList);
System.out.println(str);
System.out.println("Exit");
}
public static String regReplaceImage(String content, List<String> picList)
{
String pattern="<img.*?src=['\"]{1}([^l].*?)['\"]{1}.*?>";
StringBuffer operatorStr = new StringBuffer(content);
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(content);
int i = 0;
while(m.find()) {
operatorStr.replace(m.start(1), m.end(1), picList.get(i));
m = p.matcher(operatorStr);
i ++;
if(i == picList.size())
i = 0;
}
return operatorStr.toString();
}
}
程序输出:
<img src="liangruibo/22222.png" width="100" />
<img src="liangruibo/33333.png" width="100" />
<img src="liangruibo/44444.png" width="100" />
<img src="liangruibo/55555.png" width="100" />