近来写自动化程序用到了Java正则表达式,一个小坑记录一下
Java正则表达式的用法有很多文章会写,基本上是这样子的:
String normalReg = "\\d{5}";
Pattern p = Pattern.compile(normalReg);
Matcher m = p.matcher(srcStr);
然后就可以用Matcher的对象m做很多事情啦。写一个函数先:
public static void testReg(String srcStr) {
String normalReg = "\\d{5}";
Pattern p = Pattern.compile(normalReg);
Matcher m = p.matcher(srcStr);
if (m.find()) {
System.out.println(m.group(0));
}
}
然后调用它,
String test = "aaa1234567bbb";
testReg(test);
输出为:12345
对象m还可以调用matches(),将函数testReg中的m.find() 改成m.matches()看看啥效果,运行,等待,等待,会发现是空的。
查看下文档,发现matches()只有字符串全部匹配才返回true,否则返回false,而可爱的find()不同,它是只要有匹配的就要返回,所以要想matches()返回true,可以改下正则表达式为:
String normalReg = "\\w*\\d*\\w*";
再运行一下,可以看到结果为:aaa1234567bbb,也就是要匹配的原始字符串。
但是我的需求其实没有这么简单。我要匹配的字符串其实是这个样子的:
String time = "\"effectiveStartTime\": \"$current_time+1h\",\"effectiveEndTime\": \"$current_time\";
匹配所有以$current_time开头的字符串,如果后面没有+或者-,就返回当前时间,否则就按照算式返回计算后的时间。
如果只是匹配一个,那么可以很简单的用find()和replace搞定,也就是大概这样:
public static String testReg(String srcStr) {
char op;
char unit;
int offset;
String result;
String timespan = null;
Instant inst = Instant.now();
Instant inst1 = null;
StringBuffer sb = new StringBuffer();
String normalReg = "(\\$current_time)\\s*([+|-]?)\\s*(\\d*)\\s*([d|h|m]?)";
Pattern p = Pattern.compile(normalReg);
Matcher m = p.matcher(srcStr);
if (m.find()) {
try {
if (m.group(2).equals("")) {
return m.replaceFirst(String.valueOf(System.currentTimeMillis()));
}
op = m.group(2).charAt(0);
offset = Integer.parseInt(m.group(3));
unit = m.group(4).charAt(0);
if (op == '+') {
switch (unit) {
case 'm':
inst1 = inst.plus(Duration.ofMinutes(offset));
break;
case 'h':
inst1 = inst.plus(Duration.ofHours(offset));
break;
case 'd':
inst1 = inst.plus(Duration.ofDays(offset));
break;
default:
inst1 = inst;
}
} else if (op == '-') {
switch (unit) {
case 'm':
inst1 = inst.minus(Duration.ofMinutes(offset));
break;
case 'h':
inst1 = inst.minus(Duration.ofHours(offset));
break;
case 'd':
inst1 = inst.minus(Duration.ofDays(offset));
break;
default:
inst1 = inst;
}
}
if (inst1 == null)
return srcStr;
timespan = String.valueOf(inst1.getEpochSecond()).concat("000");
return m.replaceFirst(timespan);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
return srcStr;
}
运行一下看看吧:
String time = "\"effectiveStartTime\": \"$current_time+1h\",\"effectiveEndTime\": \"$current_time\",aaaaaaaaaa:\"$current_time\"";
System.out.println(testReg(time));
运行结果为:"effectiveStartTime": "1547208757000","effectiveEndTime": "$current_time",aaaaaaaaaa:"$current_time"
后边的都没有替换,当然可以将replaceFirst改成replaceAll,然后惊喜的发现都替换了,可是呢,替换的时间并不对,这个时候就该appendReplacement()上场了。
appendReplacement()的作用我觉得可以这样理解,利用正则表达式去匹配源字符串,发现一个匹配的字符串,就运行一次计算,然后将结果加到stringbuffer字符串中,然后继续向下匹配一直到最后,但是它并不会把最后匹配的字符串之后的字符加到stringbuffer中,于是还需要另外一个appendTail()来帮忙,所以改造一下函数吧:
public static String testReg(String srcStr) {
char op;
char unit;
int offset;
String timespan = null;
Instant inst = Instant.now();
Instant inst1 = null;
StringBuffer sb = new StringBuffer();
String normalReg = "(\\$current_time)\\s*([+|-]?)\\s*(\\d*)\\s*([d|h|m]?)";
Pattern p = Pattern.compile(normalReg);
Matcher m = p.matcher(srcStr);
while (m.find()) {
try {
if (m.group(2).equals("")) {
m.appendReplacement(sb,String.valueOf(System.currentTimeMillis()));
continue;
}
op = m.group(2).charAt(0);
offset = Integer.parseInt(m.group(3));
unit = m.group(4).charAt(0);
if (op == '+') {
switch (unit) {
case 'm':
inst1 = inst.plus(Duration.ofMinutes(offset));
break;
case 'h':
inst1 = inst.plus(Duration.ofHours(offset));
break;
case 'd':
inst1 = inst.plus(Duration.ofDays(offset));
break;
default:
inst1 = inst;
}
} else if (op == '-') {
switch (unit) {
case 'm':
inst1 = inst.minus(Duration.ofMinutes(offset));
break;
case 'h':
inst1 = inst.minus(Duration.ofHours(offset));
break;
case 'd':
inst1 = inst.minus(Duration.ofDays(offset));
break;
default:
inst1 = inst;
}
}
if (inst1 == null)
return srcStr;
timespan = String.valueOf(inst1.getEpochSecond()).concat("000");
m.appendReplacement(sb, timespan);
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
m.appendTail(sb);
return sb.toString();
}
再调用下试试:
String time = "\"effectiveStartTime\": \"$current_time+1h\",\"effectiveEndTime\": \"$current_time\",aaaaaaaaaa:\"$current_time\"";
System.out.println(testReg(time));
运行结果为: "effectiveStartTime": "1547209178000","effectiveEndTime": "1547205578660",aaaaaaaaaa:"1547205578660"
每个都替换了,而且还没有错,就是这样了。