需求
指標基本格式:
clm.{type}.{hostId}.$metricItem
示例1:
// 待匹配表達式:<hostId: 為36位的UUID>
summarize(clm.pm.xxx.yyy.uuu.zzz.0193f1b3-7bcc-4374-8546-8e87b7276003.agg.cpu.percent-avg.system,'10min','avg',true)
希望匹配出:
- type: pm.xxx.yyy.uuu.zzz
- hostId: 0193f1b3-7bcc-4374-8546-8e87b7276003
返回的Map為:
{hostId=0193f1b3-7bcc-4374-8546-8e87b7276003, type=pm.xxx.yyy.uuu.zzz}
示例2:
// 待匹配表達式:<hostId: 為36位的UUID>
summarize(clm.pm.0193f1b3-7bcc-4374-8546-8e87b7276003.agg.cpu.percent-avg.system,'10min','avg',true)
希望匹配出:
- type: pm
- hostId: 0193f1b3-7bcc-4374-8546-8e87b7276003
返回的Map為:
{hostId=0193f1b3-7bcc-4374-8546-8e87b7276003, type=pm}
示例3:
// 待匹配表達式:<hostId: 使用 "_"分割的IP地址>
summarize(clm.pm.xxx.yyy.uuu.zzz.10_144_202_141.agg.cpu.percent-avg.system,'10min','avg',true)
希望匹配出:
- type: pm.xxx.yyy.uuu.zzz
- hostId: 0193f1b3-7bcc-4374-8546-8e87b7276003
返回的Map為:
{hostId=10_144_202_141, type=pm.xxx.yyy.uuu.zzz}
基本思路
使用正則表達式的group()去獲取結果;
- group()正則表示中,使用小括號
()
表示不同的group,有多少個開括號(
,就會有多少個組,表示為:group(0)、group(1)、group(N); - group()正則表示中,可以為不同的group添加名稱,如
<type>
或<hostId>
,方便匹配;
對於示例1
和示例2
,可使用下面的group()表達式去匹配:
// hostId: 為36位的UUID
"^.*clm\\.(?<type>\\w+\\.*.*)\\.(?<hostId>\\w{8}(?:-\\w{4}){3}-\\w{12}?)"
解析:
上面包括多個組,下面僅介紹感興趣的組,比如:
1. (?<type>\\w+\\.*.*) : 別名為 type,匹配此()內的值;
2. (?<hostId>\\w{8}(?:-\\w{4}){3}-\\w{12}?): 別名為 hostId,匹配此()內的值(注:為最外層的小括號)
對於示例3
,可使用下面的group()表達式去匹配:
// hostId: 使用 "_"分割的IP地址
"^.*clm\\.(?<type>\\w+\\.*.*)\\.(?<hostId>\\d{0,3}_\\d{0,3}_\\d{0,3}_\\d{0,3})"
代碼實現
RegexGroupUtils.java
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexGroupUtils {
private static final Pattern groupAlias = Pattern.compile("\\<(.*?)\\>");
/**
* 使用正則表達式的group,查找出String中的參數值;<br/>
* 示例如下:
* <pre>{@code
* // 格式:
* clm.{type}.{hostId}.$metricItem
*
* // 待匹配表達式:<hostId: 為36位的UUID>
* summarize(clm.pm.xxx.yyy.uuu.zzz.0193f1b3-7bcc-4374-8546-8e87b7276003.agg.cpu.percent-avg.system,'10min','avg',true)
*
* 希望匹配出:
* - type: pm.xxx.yyy.uuu.zzz
* - hostId: 0193f1b3-7bcc-4374-8546-8e87b7276003
*
* 返回的Map為:
* {hostId=0193f1b3-7bcc-4374-8546-8e87b7276003, type=pm.xxx.yyy.uuu.zzz}
* }</pre>
*
* @param sourceString 待匹配的字符串
* @param pattern 正則表達式
* @return
*/
public static Map<String, String> findParams(String pattern, String sourceString) {
Map<String, String> mapResult = Maps.newConcurrentMap();
if (Strings.isNullOrEmpty(sourceString) || ObjectUtils.isEmpty(pattern)) {
return mapResult;
}
// 找出pattern中以"<xxxx>"表示的組別名
Set<String> groupNames = PlaceHolderReplaceUtils.findPlaceHolderKeys(pattern, groupAlias);
if (CollectionUtils.isEmpty(groupNames)) {
return mapResult;
}
// 創建 Pattern 對象
Pattern r = Pattern.compile(pattern);
// 現在創建 matcher 對象
Matcher m = r.matcher(sourceString);
if (m.find()) {
for (String param : groupNames) {
String value = m.group(param);
if (!Strings.isNullOrEmpty(value)) {
mapResult.put(param, value);
}
}
}
return mapResult;
}
}
PlaceHolderReplaceUtils.java
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Desc: 占位符替換, 占位符表示為:{@code {placeholder}};
* <p>
* 示例:替換如下{xxx}占位符中的內容
* <pre>"名字:{name},年齡:{age},學校:{school}"</pre>
*/
public class PlaceHolderReplaceUtils {
private static final Pattern pattern = Pattern.compile("\\{(.*?)\\}");
private static Matcher matcher;
/**
* 查找String中的占位符keys;<br/>
* 示例: "名字:{name},年齡:{age},學校:{school}", 則返回:Set[name,age,school]
* <p>
* pattern示例:
* <pre> {@code
* // 尖括號:<placeHolder> 表示為占位符
* Pattern pattern = Pattern.compile("\\<(.*?)\\>");
*
* // 大括號:{placeHolder} 表示為占位符, 上面的示例中就使用{}作為占位符
* Pattern pattern = Pattern.compile("\\{(.*?)\\}");
* }
* </pre>
*
* @param sourceString
* @param pattern
* @return
*/
public static Set<String> findPlaceHolderKeys(String sourceString, Pattern pattern) {
Set<String> placeHolderSet = Sets.newConcurrentHashSet();
if (Strings.isNullOrEmpty(sourceString) || ObjectUtils.isEmpty(pattern)) {
return placeHolderSet;
}
String targetString = sourceString;
matcher = pattern.matcher(sourceString);
while (matcher.find()) {
String key = matcher.group(); //示例: {name}
String placeHolder = key.substring(1, key.length() - 1).trim(); //示例: name
placeHolderSet.add(placeHolder);
}
return placeHolderSet;
}
}
測試代碼
import org.junit.Test;
public class RegexGroupUtilsTest {
@Test
public void findParams() {
//-----------start 測試匹配 uuid(32-36位)
String line1 = "summarize(clm.pm.xxx.yyy.uuu.zzz.0193f1b3-7bcc-4374-8546-8e87b7276003.agg.cpu.percent-avg.system,'10min','avg'," +
"true)";
String line2 = "summarize(clm.pm.0193f1b3-7bcc-4374-8546-8e87b7276003.agg.cpu.percent-avg.system,'10min','avg',true)";
String pattern = "^.*clm\\.(?<type>\\w+\\.*.*)\\.(?<hostId>\\w{8}(?:-\\w{4}){3}-\\w{12}?)"; //OK
System.out.println("-----line1--------");
System.out.println(RegexGroupUtils.findParams(pattern, line1).toString());
System.out.println("-----line2--------");
System.out.println(RegexGroupUtils.findParams(pattern, line2).toString());
//-----------end 測試匹配 uuid(32-36位)
//-----------start 測試匹配 ip(下划線分割)
System.out.println("----------------IP------------");
String lineIp = "summarize(clm.pm.xxx.yyy.uuu.zzz.10_144_202_141.agg.cpu.percent-avg.system,'10min','avg'," +
"true)";
String patternIp = "^.*clm\\.(?<type>\\w+\\.*.*)\\.(?<hostId>\\d{0,3}_\\d{0,3}_\\d{0,3}_\\d{0,3})";
System.out.println(RegexGroupUtils.findParams(patternIp, lineIp).toString());
//-----------start 測試匹配 ip(下划線分割)
}
/**
* 輸出:
*
* -----line1--------
* {hostId=0193f1b3-7bcc-4374-8546-8e87b7276003, type=pm.xxx.yyy.uuu.zzz}
* -----line2--------
* {hostId=0193f1b3-7bcc-4374-8546-8e87b7276003, type=pm}
* ----------------IP------------
* {hostId=10_144_202_141, type=pm.xxx.yyy.uuu.zzz}
*/
}