[英]Splitting Strings in a stream in Java?
我有一个任务,我们正在阅读文本文件并计算每个单词的出现次数(忽略标点符号)。 我们不必使用流,但我想练习使用它们。
到目前为止,我能够读取一个文本文件并将每一行放在一个字符串中,并使用这个列表中的所有字符串:
try (Stream<String> p = Files.lines(FOLDER_OF_TEXT_FILES)) {
list = p.map(line -> line.replaceAll("[^A-Za-z0-9 ]", ""))
.collect(Collectors.toList());
}
但是,到目前为止,它只是将所有行都变成了一个字符串,因此列表的每个元素都不是一个单词,而是一行。 有没有一种使用流的方法,我可以让每个元素都是一个单词,使用字符串的 split 方法和正则表达式? 还是我必须在 stream 本身之外处理这个问题?
我可能误解了你的问题。 但是,如果您只想用逗号分隔单词,可以尝试下面的代码将line.replaceAll("[^A-Za-z0-9 ]", "")
替换为Arrays.asList(line.replaceAll("[^A-Za-z0-9 ]", "").split(" ")).stream().collect(Collectors.joining(","))
再次使用列表上的加入方法来获取逗号分隔的单词字符串。
String commaSeperated = list.stream().collect(Collectors.joining(",")) ;
您可以根据您的要求对最终字符串执行进一步的操作。
尝试这个:
String fileName = "file.txt";
try {
Map<String, Long> wordCount = Files.lines(Path.of(fileName))
.flatMap(line -> Arrays.stream(line.split("\\s+")))
.filter(w->w.matches("[a-zA-Z]+"))
.sorted(Comparator.comparing(String::length)
.thenComparing(String.CASE_INSENSITIVE_ORDER))
.collect(Collectors.groupingBy(w -> w,
LinkedHashMap::new, Collectors.counting()));
wordCount.entrySet().forEach(System.out::println);
}catch (Exception e) {
e.printStackTrace();
}
这个比较简单。 它只是在空白处拆分并通过将它们放在 map 中来计算单词,其中 Key 是单词,Value 是包含计数的 long。
我包括了一个过滤器,只捕获只有字母的单词。 其工作方式是将Lines
放入 stream。 然后使用String.split
将每一行拆分为单词。 由于这会创建一个数组,因此flatMap
会将所有这些单独的单词流转换为一个 stream 来处理它们。 这个工作的马是Collectors.groupingBy
它将以特定方式对每个键的值进行分组。 在这种情况下,我指定了Collectors.counting()
方法以在每次出现键(即单词)时增加计数。
作为一种选择,我先按长度排序单词,然后按字母顺序排序,忽略大小写。
不要在一行上应用replaceAll
,而是在该行的单词上执行如下操作:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
String str = "Harry is a good cricketer. Tanya is an intelligent student. Bravo!";
List<String> words = Arrays.stream(str.split("\\s+")).map(s -> s.replaceAll("[^A-Za-z0-9 ]", ""))
.collect(Collectors.toList());
System.out.println(words);
}
}
Output:
[Harry, is, a, good, cricketer, Tanya, is, an, intelligent, student, Bravo]
注意:正则表达式\\s+
在空格上拆分字符串。
首先,对于每一行,我们删除所有非字母数字字符(不包括空格),然后我们按空格分割,因此所有元素都是单个单词。 由于我们是平面映射,因此 stream 包含所有单词。 然后我们简单地使用groupingBy
收集器进行收集,并使用counting()
作为下游收集器。 这将给我们留下一个Map<String, Long>
键是单词,值是出现的次数。
list = p
.flatMap(line -> Arrays.stream(line.replaceAll("[^0-9A-Za-z ]+", "").split("\\s+")))
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting());
由于要处理单词时行边界无关紧要,因此首选方法是不打扰拆分为行,只需将行拆分为单词,而是首先将文件拆分为单词。 你可以使用类似的东西:
Map<String,Long> wordsAndCounts;
try(Scanner s = new Scanner(Paths.get(path))) {
wordsAndCounts = s.findAll("\\w+")
.collect(Collectors.groupingBy(MatchResult::group, Collectors.counting()));
}
wordsAndCounts.forEach((w,c) -> System.out.println(w+":\t"+c));
Scanner
的findAll
方法需要 Java 9 或更高版本。 此答案包含 Java 8 的findAll
实现。这允许在 Java 8 上使用它,并只需切换到标准方法即可轻松迁移到较新版本。
可以使用Pattern.splitAsStream
以高效的方式拆分字符串,同时在创建出现计数的 map 之前替换所有非单词字符:
Pattern splitter = Pattern.compile("(\\W*\\s+\\W*)+");
String fileStr = Files.readString(Path.of(FOLDER_OF_TEXT_FILES));
Map<String, Long> collect = splitter.splitAsStream(fileStr)
.collect(groupingBy(Function.identity(), counting()));
System.out.println(collect);
对于非单词字符的拆分和删除,我们使用模式(\W*\s+\W*)+
来查找可选的非单词字符,然后是空格,然后再查找可选的非单词字符。
对于整个“读取文本文件并使用流计算每个单词”,我建议使用如下内容:
try (Stream<String> lines = Files.lines(FOLDER_OF_TEXT_FILES)) {
lines.flatMap(l -> Arrays.stream(l.split(" ")))
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
无需先将所有内容收集到列表中,这可以内联完成。
您使用 try-with-resources 也很好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.