繁体   English   中英

在 Java 中拆分 stream 中的字符串?

[英]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));

ScannerfindAll方法需要 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM