我试图用简单的Java 8 lambdas / streams解决方案来计算正则表达式模式的匹配数。例如,对于这种模式/匹配器:
curl -XPUT localhost:9200/crud_sample/Customer_Info/1 -d '{
"name": "PH"
}'
方法final Pattern pattern = Pattern.compile("\\d+");
final Matcher matcher = pattern.matcher("1,2,3,4");
分割给定模式上的文本而不是匹配模式。虽然它很优雅并且保留了不变性,但它并不总是正确的:
splitAsStream
我也试过(ab)使用// count is 4, correct
final long count = pattern.splitAsStream("1,2,3,4").count();
// count is 0, wrong
final long count = pattern.splitAsStream("1").count();
。问题是我必须猜测我应该多少次调用IntStream
而不是直到它返回false。
matcher.find()
我熟悉传统解决方案final long count = IntStream
.iterate(0, i -> matcher.find() ? 1 : 0)
.limit(100)
.sum();
,其中while (matcher.find()) count++;
是可变的。使用Java 8 lambdas / streams有一种简单的方法吗?
答案 0 :(得分:4)
要正确使用Pattern::splitAsStream
,您必须反转正则表达式。这意味着您应该使用\\d+
而不是\\D+
(将在每个数字上拆分)。这为你的字符串提供了数字。
final Pattern pattern = Pattern.compile("\\D+");
// count is 4
long count = pattern.splitAsStream("1,2,3,4").count();
// count is 1
count = pattern.splitAsStream("1").count();
答案 1 :(得分:3)
Pattern.splitAsStream的javadoc中相当人为的语言可能是罪魁祸首。
此方法返回的流包含输入序列的每个子字符串,由与此模式匹配的另一个子序列终止,或者由输入序列的末尾终止。
如果您打印出1,2,3,4
的所有匹配项,您可能会惊讶地发现它实际上正在返回逗号,而不是数字。
System.out.println("[" + pattern.splitAsStream("1,2,3,4")
.collect(Collectors.joining("!")) + "]");
打印[!,!,!,]
。奇怪的是它给你4
而不是3
的原因。
显然,这也解释了为什么"1"
给出了0
,因为字符串中数字之间没有字符串。
快速演示:
private void test(Pattern pattern, String s) {
System.out.println(s + "-[" + pattern.splitAsStream(s)
.collect(Collectors.joining("!")) + "]");
}
public void test() {
final Pattern pattern = Pattern.compile("\\d+");
test(pattern, "1,2,3,4");
test(pattern, "a1b2c3d4e");
test(pattern, "1");
}
打印
1,2,3,4-[!,!,!,]
a1b2c3d4e-[a!b!c!d!e]
1-[]
答案 2 :(得分:3)
您可以扩展AbstractSpliterator
来解决此问题:
static class SpliterMatcher extends AbstractSpliterator<Integer> {
private final Matcher m;
public SpliterMatcher(Matcher m) {
super(Long.MAX_VALUE, NONNULL | IMMUTABLE);
this.m = m;
}
@Override
public boolean tryAdvance(Consumer<? super Integer> action) {
boolean found = m.find();
if (found)
action.accept(m.groupCount());
return found;
}
}
final Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("1");
long count = StreamSupport.stream(new SpliterMatcher(matcher), false).count();
System.out.println("Count: " + count); // 1
matcher = pattern.matcher("1,2,3,4");
count = StreamSupport.stream(new SpliterMatcher(matcher), false).count();
System.out.println("Count: " + count); // 4
matcher = pattern.matcher("foobar");
count = StreamSupport.stream(new SpliterMatcher(matcher), false).count();
System.out.println("Count: " + count); // 0
答案 3 :(得分:1)
很快,您有一个stream of String
和一个String pattern
:这些字符串中有多少与此模式匹配?
final String myString = "1,2,3,4";
Long count = Arrays.stream(myString.split(","))
.filter(str -> str.matches("\\d+"))
.count();
其中第一行可以是流List<String>().stream()
,...
我错了吗?
答案 4 :(得分:0)
您可以使用Matcher#results()
来获取所有匹配项:
Stream<MatchResult>
results()
返回与模式匹配的输入序列的每个子序列的匹配结果流。匹配结果的顺序与输入序列中匹配的子序列的顺序相同。
基于使用反向模式的另一个简单解决方案:
String pattern = "\\D+";
System.out.println("1".replaceAll("^" + pattern + "|" + pattern + "$", "").split(pattern, 0).length); // => 1
这里,所有非数字都从字符串的开头和结尾删除,然后字符串被非数字序列拆分而不报告任何空的尾随空格元素(因为0
作为< em>限制参数split
)。
请参阅this demo:
String pattern = "\\D+";
System.out.println("1".replaceAll("^" + pattern + "|" + pattern + "$", "").split(pattern, 0).length); // => 1
System.out.println("1,2,3".replaceAll("^" + pattern + "|" + pattern + "$", "").split(pattern, 0).length);// => 3
System.out.println("hz 1".replaceAll("^" + pattern + "|" + pattern + "$", "").split(pattern, 0).length); // => 1
System.out.println("1 hz".replaceAll("^" + pattern + "|" + pattern + "$", "").split(pattern, 0).length); // => 1
System.out.println("xxx 1 223 zzz".replaceAll("^" + pattern + "|" + pattern + "$", "").split(pattern, 0).length);//=>2