![](/img/trans.png)
[英]Kafka Streams API: write to sink if .groupByKey().count() matches itemCounter
[英]Count regex matches with streams
我试图用简单的Java 8 lambdas / stream解决方案来计算正则表达式模式的匹配数。 例如,对于此模式/匹配器:
final Pattern pattern = Pattern.compile("\\d+");
final Matcher matcher = pattern.matcher("1,2,3,4");
splitAsStream
方法splitAsStream
定模式上的文本分割而不是匹配模式。 虽然它很优雅并且保留了不变性,但它并不总是正确的:
// 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();
我也试过(ab)使用IntStream
。 问题是我必须猜测我应该多少次调用matcher.find()
而不是它返回false。
final long count = IntStream
.iterate(0, i -> matcher.find() ? 1 : 0)
.limit(100)
.sum();
我熟悉传统的解决方案while (matcher.find()) count++;
count
是可变的。 使用Java 8 lambdas / streams有一种简单的方法吗?
要正确使用Pattern::splitAsStream
,您必须反转正则表达式。 这意味着你不应该使用\\\\D+
\\\\d+
(它会在每个数字上分开),而应该使用\\\\D+
。 这为您提供了String中的编号。
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();
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-[]
您可以扩展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
不久,您有一个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()
, ...
我错了吗 ?
您可以使用Matcher#results()
来获取所有匹配项:
Stream<MatchResult>
results()
返回与模式匹配的输入序列的每个子序列的匹配结果流 。 匹配结果的顺序与输入序列中的匹配子序列的顺序相同。
基于使用反向模式的另一个简单解决方案:
String pattern = "\\D+";
System.out.println("1".replaceAll("^" + pattern + "|" + pattern + "$", "").split(pattern, 0).length); // => 1
这里,所有非数字都从字符串的开头和结尾删除,然后字符串被非数字序列拆分而不报告任何空的尾随空格元素(因为0
作为限制参数传递给split
)。
看这个演示 :
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.