Java 正则表达式的用法和具体实例
目录
正则表达式常用符号:
字符 | 说明 |
^ | 匹配输入字符串的开始位置。要匹配 "^" 字符本身,请使用 "\^" |
$ | 匹配输入字符串的结尾位置。要匹配 "$" 字符本身,请使用 "\$" |
( ) | 标记一个子表达式的开始和结束位置。要匹配小括号,前面加 \ |
[ ] | 用来自定义能够匹配 '多种字符' 的表达式。要匹配中括号,前面加 \ |
{ } | 修饰匹配次数的符号。要匹配大括号,请使用 "\{" 和 "\}" |
. | 匹配除了换行符(\n)以外的任意一个字符。要匹配小数点本身,请使用 "\." |
? | 修饰匹配次数为 0 次或 1 次。要匹配 "?" 字符本身,请使用 "\?" |
+ | 修饰匹配次数为至少 1 次。要匹配 "+" 字符本身,请使用 "\+" |
* | 修饰匹配次数为 0 次或任意次。要匹配 "*" 字符本身,请使用 "\*" |
| | 左右两边表达式之间 "或" 关系。匹配 "|" 本身,请使用 "\|" |
{n} | 修饰匹配次数为n次 |
{n,m} | 修饰匹配次数为n次到m次 |
Java 中正常表达式的应用:
在实际的应用中,如何写pattern 是难点,需要熟悉正则表达式的常用符号,应用的话,需要了解其用法。
实例1:匹配和替换字符
要求:
根据字段的值(Map<String, Object> param)进行动态匹配后,再执行sql,得到结果
Sect* from xxx where field_name1=$param.paramName and field_name2=$global.STAFF_ID
思考:
这边要进行替换的值是 $param.paramName,“$”开头,中间是“.”,要匹配的值是 逗号后面的值。
String pattern = "\\$[a-zA-Z_]*\\.([a-zA-Z_]*)";
“\\” 是转义, ()用于标记要匹配的值
代码:
public static void main(String[] args) {
getMatchReplace();
}
public static void getMatchReplace()
{
Map<String, Object> param = new HashMap<>();
param.put("paramName", "lily");
param.put("STAFF_ID", "1222");
String content = "select * from xxx where field_name1 = $param.paramName and field_name2 = $global.STAFF_ID globals";
String pattern = "\\$[a-zA-Z_]*\\.([a-zA-Z_]*)";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(content);
StringBuffer sb = new StringBuffer();
while (m.find())
{
String key = m.group(1);
String value = MapUtils.getString(param, key);
m.appendReplacement(sb, value == null ? "" : value);
}
System.out.println("appendTail before: "+ sb.toString());
m.appendTail(sb);
System.out.println("appendTail later: "+sb.toString());
}
结果:
appendTail before: select * from xxx where field_name1 = lily and field_name2 = 1222
appendTail later: select * from xxx where field_name1 = lily and field_name2 = 1222 globals
分析:
正则表达式有分组概念:
整个正则默认为0,而其余组号从“(”开始分配,第1组是第一个(和与其对应的)之间的正则内容。m.group(0)没有给参数,匹配的是0分组即整个正则;m.group(1)给了参数1,则是从整个正则匹配到的内容里面第一个()里面的内容,如例子中的([a-zA-Z_]*)。
appendReplacement方法:
sb是一个StringBuffer,replaceContext待替换的字符串,这个方法会把匹配到的内容替换为replaceContext,并且把从上次替换的位置到这次替换位置之间的字符串也拿到,然后,加上这次替换后的结果一起追加到StringBuffer里(假如这次替换是第一次替换,那就是只追加替换后的字符串)。
比如上面的例子:value拿到lily后,appendReplacement 会找到匹配模式,会把$开头的$param.paramName找到逗号后面的paramName(m.group(1))替换成lily。
appendTail方法:
sb是一个StringBuffer,这个方法是把最后一次匹配到内容之后的字符串追加到StringBuffer中。即把后面没检测过的字符串全加上去。
比如执行appendTail之前,sb的值是“select * from account where field_name1 = lily and field_name2 = 1222”,加来appendTail把剩余的值追加后面。就变成了“select * from account where field_name1 = lily and field_name2 = 1222 globals”
例子2:转化字符
要求:
对查询的名字,转化特殊字符,查到值后,再将查询的名字转为回来。
思考:
需要弄个转化,考虑把特殊字符16进制,前面加个标记,比如\u。转化的时候,再把十六进制替换回来,再匹配\u。
代码:
public static void main(String[] args) {
String infos = convertString("1222#WOLF_KING@27");
System.out.println("change infos: " + infos);
System.out.println("rechange infos: " + parseHexStr(infos));
}
// 转化部分特殊字符
public static String convertString(final String s) {
if (Objects.isNull(s)) {
throw new IllegalArgumentException("Invalid configName: null");
}
String t = StringUtils.replace(s, "/", "/");
final int n = t.length();
StringBuilder cc = new StringBuilder(50);
for (int i = 0; i < n; i++) {
final char c = t.charAt(i);
if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '-' || c == '_'
|| c == '.' || Character.isLetter(c))) {
cc.append("/u" + StringUtils.leftPad(Integer.toHexString(c), 4, '0'));
} else {
cc.append(c);
}
}
return cc.toString();
}
//将带有十六进制的内容转化成十进制
private static String parseHexStr(String hexValue) {
String patternStr = "\\/u(\\d{4})";
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(hexValue);
String str = hexValue;
while (matcher.find()) {
String group0 = matcher.group();
System.out.println("整个表达式: "+group0);
String group1 = matcher.group(1);
System.out.println("要匹配的值: "+group1);
String str8 = StringUtils.leftPad(group1, 8, "0");
System.out.println("往左边的内容补0,补齐到8位: "+str8);
String replacement = String.valueOf((char) Integer.parseInt(str8, 16));
System.out.println("把16进制的数字转化为要替换的内容: "+replacement);
str = str.replaceAll(group0, replacement);
System.out.println("替换后的值: "+str);
System.out.println("==== next ===== ");
}
return str;
}
结果:
change infos: 1222/u0023WOLF_KING/u004027
整个表达式: /u0023
要匹配的值: 0023
往左边的内容补0,补齐到8位: 00000023
把16进制的数字转化为要替换的内容: #
替换后的值: 1222#WOLF_KING/u004027
==== next =====
整个表达式: /u0040
要匹配的值: 0040
往左边的内容补0,补齐到8位: 00000040
把16进制的数字转化为要替换的内容: @
替换后的值: 1222#WOLF_KING@27
==== next =====
rechange infos: 1222#WOLF_KING@27
后面再添加一些工作上遇到的例子
总结:
在使用正则表达匹配和替换中,难点是pattern要怎么写,需要多熟悉,多练习。至于怎么用,了解分组的概念,替换基本就可以了。
替换的方式有很多,根据要求可以使用不同的替换方式,比如springEL,StrSubstitutor等