我有一个如下字符串:
SOMETEXT(ABC, DEF, 5, 78.0, MNO)
我想用正则表达式解析它以获得ABC,DEF和MNO的List<String>
。即。我想避免任何类型的数字,只提取文本。
总的来说,我的结构如下:
class Detail {
String name;
String type;
}
// Sample values of name = "test1" type = "SOMETEXT(ABC,5)"
// Sample values of name = "test2" type = "SOMETEXT(ABC,DEF,2.2)"
// Sample values of name = "test3" type = "SOMETEXT(ABC,DEF)"
从List<Detail>
我想得到Map<String, List<String>>
其中list<String>
提取类型和密钥的文本为name
,如果可能,使用流的java 8方式。
直到现在我必须从字符串中获得第一个文本,我按照以下方式执行:
Map<String, List<String>> assignOperatorMap = details
.stream()
.collect(groupingBy(md -> md.getName(), mapping((Details m) ->
m.getType().substring(m.getType().indexOf("(") + 1,
m.getType().indexOf(")")).split("\\,")[0] ,
Collectors.toList()
)));
以上代码给了我:
{test1=[ABC], test2=[ABC], test3=[ABC]}
只是第一个值。
答案 0 :(得分:0)
如果订单无关紧要,你可以尝试这样的事情:
final List<Detail> details = Arrays.asList(
new Detail("test1", "SOMETEXT(ABC, DFD)"),
new Detail("test2", "SOMETEXT(ABC,DEF,2.2)"),
new Detail("test3", "SOMETEXT(ABC,DEF,GHF)")
);
final Map<String, List<String>> map = details
.stream()
.collect(Collectors.groupingBy(
Detail::getName,
Collectors.mapping(
detail -> {
final String[] values = detail.getType().split("[,(). 0-9]+");
return Arrays.copyOfRange(values, 1, values.length);
},
Collector.of(ArrayList::new,
(list, array) -> list.addAll(Arrays.asList(array)),
(source, target) -> {
source.addAll(target);
return source;
}
)
)
));
System.out.println(map);
// Output: {test2=[ABC, DEF], test3=[ABC, DEF, GHF], test1=[ABC, DFD]}
答案 1 :(得分:0)
这个怎么样:
List<Detail> details = new ArrayList<>();
details.add(new Detail("test1", "SOMETEXT(ABC,5)"));
details.add(new Detail("test2", "SOMETEXT(ABC,DEF,2.2)"));
details.add(new Detail("test3", "SOMETEXT(ABC,DEF)"));
Map<String, List<String>> assignOperatorMap = details.stream()
.flatMap(d -> Arrays.stream(d.getType()
.replaceAll("\\w+\\((.*)\\)", "$1")
.split(","))
.filter(s -> s.matches("[A-Za-z_]+"))
.map(s -> new SimpleEntry<>(d.getName(), s)))
.collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList())));
System.out.println(assignOperatorMap); // {test2=[ABC, DEF], test3=[ABC, DEF], test1=[ABC]}
我们的想法是首先在括号中捕获字符串:.replaceAll("\\w+\\((.*)\\)", "$1")
,然后将其拆分为,
并过滤掉与[A-Za-z_]+
不匹配的内容。
还有一个创建一堆Entry<String, String>
(名称,类型)的技巧,以避免必须流两次,因为每个Detail
现在可以产生多个类型字符串,我们必须以某种方式< em>将它们展平成List<String>
(而不是List<String[]>
)。 (最好用Java 9的flatMapping
收集器来完成,但它还没有。)
如何扩展此正则表达式以忽略某些文本,例如HOURS,MINUTES
您可以使用要忽略的字词创建Set<String>
,并在第二次filter
调用中根据该字词进行过滤:
Set<String> ignore = new HashSet<>();
ignore.add("HOURS");
ignore.add("MINUTES");
...
.filter(s -> s.matches("[A-Za-z_]+"))
.filter(s -> !ignore.contains(s)) // <-- extra filter call
.map(s -> new SimpleEntry<>(d.getName(), s)))
...