![](/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.