从可以采用多种形式并从 Java 中的 .properties 文件加载的字符串中提取值的最佳方法
Best Way to Extract Values from a String that can take many forms and is Loaded from a .properties file in Java
我有一个 ws.properties 文件,其中有一个名为 success.code 的 属性给我选择 HTTP 状态成功代码 (2xx) 的选项,除了默认的 200,我希望服务器也将其视为 "Success"。
思路是这样的:
- 如果我输入这样的值:
success.code=201,202, 203 (values separated by a comma)
负责提取这个值的代码部分应该立即明白这是三个独立的代码,提取它们中的每一个并将它们存储在列表或集合中或....
这其实很简单:
private Set<String[]> getSuccessCodes(HashMap<String, String> codes){
Set<String[]> successCodes = new HashSet<>();
String value;
for (Map.Entry<String, String> entry : codes.entrySet()) {
value = entry.getValue();
if(!StringUtils.isEmpty(value))
successCodes.add(value.split("\s*,\s*"));
}
return successCodes;
}
- 这里有点复杂:
我也想添加这些选项:
success.code=[202, 220] (two values written between hooks and separated by a comma)
- [202, 220]:当值是这样写的时候,我想让上面的方法理解我想把202到220之间的所有值都提取出来,存储起来,像我一样returned做了第一个案例。
success.code=2**
- 这意味着所有以 2 开头的代码(从 201 到 299)都应该存储在一个集合或列表中,然后 return通过该方法返回给我
success.code=[200, 205], 22*
- 这意味着我希望代码 return 给我 200 到 205 之间的所有代码(所以 200、201、202、..、2005)加上从 220 到 220 的代码。
因此,根据我为成功代码输入的值,程序应该能够从中理解要做什么和要做什么 return。
这可以实现吗?
提前致谢
与其要求你的程序接受你定义的一些复杂的语法,为什么不让框架自然地做它做的事情呢?
success.codes = 201,202,203
success.code.min = 202
success.code.max = 220
success.code.prefix = 22
然后根据需要在您的代码中组合这些属性。这是实现你想要的最简单的方法。
首先,答案肯定是是可以实现的。解决方案是您将必须编写自己的解析器或迷你解释器来获取您的 String 并首先 "compile" 它 - 即检查它是否是有效输入(假设“[”的数量等于“]”的数量并且在解析“]”的数量时任何时候都不会大于“]”的数量,即字符串“[...]][”应该被宣布为无效)。然后它会解释它。您希望您的语言越灵活,您的口译员就会越复杂。很可能正则表达式是不够的。但一般来说,您需要编写一个带有如下签名的方法:
Set<Integer> parseHttpCodesParam(String input);
您的实施将有几条规则
- 如何处理逗号分隔的整数值
- 如何处理 []
中两个逗号分隔的数字
- 如何处理像 *
这样的特殊字符
等等
如果您不介意再添加三种方法,这里是一种方法:
- 使用
"\s*,\s*(?![^\[]*\])"
拆分你的值,除非在括号中
- 遍历结果数组并映射每个元素,检查它是否包含
[
或 *
- 如果它包含
[
使用 [x,y]
中的值生成一个 IntStream.rangeClosed(x,y)
并将它们映射到它们的字符串值
- 如果它包含
*
,通过用 0
和 9
替换 *
生成一个 IntStream.rangeClosed(x00,x99)
,并将它们映射到它们的字符串值
类似于:
private static Set<String[]> getSuccessCodes(HashMap<String, String> codes){
Set<String[]> successCodes = new HashSet<>();
String value;
for (Map.Entry<String, String> entry : codes.entrySet()) {
value = entry.getValue();
if(!StringUtils.isEmpty(value))
successCodes.add(parse(value));
}
return successCodes;
}
public static String[] parse(String val) {
String[] parts = val.split("\s*,\s*(?![^\[]*\])");
return Arrays.stream(parts)
.map(e -> e.contains("[")? getRangeCodes(e): e.contains("*")? getWildCCodes(e): e)
.collect(Collectors.joining(",")).split("\s*,\s*");
}
private static String getRangeCodes(String e) {
e = e.substring(1, e.length()-1);
return IntStream.rangeClosed(Integer.parseInt(e.split("\s*,\s*")[0]), Integer.parseInt(e.split("\s*,\s*")[1]))
.mapToObj(i -> String.valueOf(i))
.collect(Collectors.joining(","));
}
private static String getWildCCodes(String e) {
return IntStream.rangeClosed(Integer.parseInt(e.replace('*', '0')), Integer.parseInt(e.replace('*', '9')))
.mapToObj(i -> String.valueOf(i))
.collect(Collectors.joining(","));
}
测试:
public static void main(String[] args) {
HashMap<String, String> codes = new HashMap<>();
codes.put("foo", "201, 202, 203");
codes.put("bar", "[202, 220]");
codes.put("baz", "2**");
codes.put("doo", "[200, 205], 22*");
codes.put("woo", "200, 201, 202, 203,[209, 214], 29*");
Set<String[]> set = getSuccessCodes(codes);
set.forEach(s -> {System.out.println(Arrays.toString(s));});
}
我有一个 ws.properties 文件,其中有一个名为 success.code 的 属性给我选择 HTTP 状态成功代码 (2xx) 的选项,除了默认的 200,我希望服务器也将其视为 "Success"。
思路是这样的:
- 如果我输入这样的值:
success.code=201,202, 203 (values separated by a comma)
负责提取这个值的代码部分应该立即明白这是三个独立的代码,提取它们中的每一个并将它们存储在列表或集合中或....
这其实很简单:
private Set<String[]> getSuccessCodes(HashMap<String, String> codes){
Set<String[]> successCodes = new HashSet<>();
String value;
for (Map.Entry<String, String> entry : codes.entrySet()) {
value = entry.getValue();
if(!StringUtils.isEmpty(value))
successCodes.add(value.split("\s*,\s*"));
}
return successCodes;
}
- 这里有点复杂:
我也想添加这些选项:
success.code=[202, 220] (two values written between hooks and separated by a comma)
- [202, 220]:当值是这样写的时候,我想让上面的方法理解我想把202到220之间的所有值都提取出来,存储起来,像我一样returned做了第一个案例。
success.code=2**
- 这意味着所有以 2 开头的代码(从 201 到 299)都应该存储在一个集合或列表中,然后 return通过该方法返回给我
success.code=[200, 205], 22*
- 这意味着我希望代码 return 给我 200 到 205 之间的所有代码(所以 200、201、202、..、2005)加上从 220 到 220 的代码。
因此,根据我为成功代码输入的值,程序应该能够从中理解要做什么和要做什么 return。
这可以实现吗?
提前致谢
与其要求你的程序接受你定义的一些复杂的语法,为什么不让框架自然地做它做的事情呢?
success.codes = 201,202,203
success.code.min = 202
success.code.max = 220
success.code.prefix = 22
然后根据需要在您的代码中组合这些属性。这是实现你想要的最简单的方法。
首先,答案肯定是是可以实现的。解决方案是您将必须编写自己的解析器或迷你解释器来获取您的 String 并首先 "compile" 它 - 即检查它是否是有效输入(假设“[”的数量等于“]”的数量并且在解析“]”的数量时任何时候都不会大于“]”的数量,即字符串“[...]][”应该被宣布为无效)。然后它会解释它。您希望您的语言越灵活,您的口译员就会越复杂。很可能正则表达式是不够的。但一般来说,您需要编写一个带有如下签名的方法:
Set<Integer> parseHttpCodesParam(String input);
您的实施将有几条规则
- 如何处理逗号分隔的整数值
- 如何处理 [] 中两个逗号分隔的数字
- 如何处理像 * 这样的特殊字符
等等
如果您不介意再添加三种方法,这里是一种方法:
- 使用
"\s*,\s*(?![^\[]*\])"
拆分你的值,除非在括号中 - 遍历结果数组并映射每个元素,检查它是否包含
[
或*
- 如果它包含
[
使用[x,y]
中的值生成一个IntStream.rangeClosed(x,y)
并将它们映射到它们的字符串值 - 如果它包含
*
,通过用0
和9
替换*
生成一个IntStream.rangeClosed(x00,x99)
,并将它们映射到它们的字符串值
类似于:
private static Set<String[]> getSuccessCodes(HashMap<String, String> codes){
Set<String[]> successCodes = new HashSet<>();
String value;
for (Map.Entry<String, String> entry : codes.entrySet()) {
value = entry.getValue();
if(!StringUtils.isEmpty(value))
successCodes.add(parse(value));
}
return successCodes;
}
public static String[] parse(String val) {
String[] parts = val.split("\s*,\s*(?![^\[]*\])");
return Arrays.stream(parts)
.map(e -> e.contains("[")? getRangeCodes(e): e.contains("*")? getWildCCodes(e): e)
.collect(Collectors.joining(",")).split("\s*,\s*");
}
private static String getRangeCodes(String e) {
e = e.substring(1, e.length()-1);
return IntStream.rangeClosed(Integer.parseInt(e.split("\s*,\s*")[0]), Integer.parseInt(e.split("\s*,\s*")[1]))
.mapToObj(i -> String.valueOf(i))
.collect(Collectors.joining(","));
}
private static String getWildCCodes(String e) {
return IntStream.rangeClosed(Integer.parseInt(e.replace('*', '0')), Integer.parseInt(e.replace('*', '9')))
.mapToObj(i -> String.valueOf(i))
.collect(Collectors.joining(","));
}
测试:
public static void main(String[] args) {
HashMap<String, String> codes = new HashMap<>();
codes.put("foo", "201, 202, 203");
codes.put("bar", "[202, 220]");
codes.put("baz", "2**");
codes.put("doo", "[200, 205], 22*");
codes.put("woo", "200, 201, 202, 203,[209, 214], 29*");
Set<String[]> set = getSuccessCodes(codes);
set.forEach(s -> {System.out.println(Arrays.toString(s));});
}