Разделение строк в потоке в Java? - PullRequest
0 голосов
/ 26 апреля 2020

У меня есть задание, где мы читаем текстовые файлы и подсчитываем вхождения каждого слова (игнорируя пунктуацию). Нам не нужно использовать потоки, но я хочу попрактиковаться в их использовании.

Пока я могу читать текстовый файл и помещать каждую строку в строку, а все строки в списке, используя это:

try (Stream<String> p = Files.lines(FOLDER_OF_TEXT_FILES)) {
    list = p.map(line -> line.replaceAll("[^A-Za-z0-9 ]", ""))
            .collect(Collectors.toList());
}

Однако до сих пор он просто делает все строки одной строкой, поэтому каждый элемент списка - это не слово, а строка. Есть ли способ использовать потоки, чтобы каждый элемент мог быть одним словом, используя что-то вроде метода разделения String с регулярным выражением? Или мне придется обрабатывать это вне самого потока?

Ответы [ 7 ]

1 голос
/ 27 апреля 2020

Поскольку границы строк не имеют значения, когда требуется обработать слова , предпочтительным способом является не пытаться разбивать строки на строки, а просто разбивать строки на слова, а сначала разбивать файл на слова. Вы можете использовать что-то вроде:

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));

Для findAll метода Scanner требуется Java 9 или новее. Этот ответ содержит реализацию findAll для Java 8. Это позволяет использовать его на Java 8 и легко переходить на более новые версии, просто переключаясь на стандартный метод.

1 голос
/ 26 апреля 2020

попробуйте это:

    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();
        }

Это относительно просто. Он просто разбивается на пустое пространство и подсчитывает слова, помещая их в карту, где ключ - это слово, а значение - это длинная строка, содержащая счет. , Это работает так, что Lines помещается в поток. Затем каждая строка разбивается на слова, используя String.split. Поскольку это создает массив, flatMap преобразует все эти отдельные потоки слов в один поток, где они обрабатываются. Рабочей лошадкой этого является Collectors.groupingBy, который группирует значения определенным образом для каждой клавиши. В этом случае я указывал метод Collectors.counting() для увеличения счетчика каждый раз, когда появлялся ключ (т. Е. Слово).

В качестве опции я сортировал слова сначала по длине, а затем по алфавиту, игнорируя регистр.

0 голосов
/ 27 апреля 2020

Для всего «прочитайте текстовый файл и посчитайте каждое слово, используя потоки», я предлагаю использовать что-то вроде этого:

try (Stream<String> lines = Files.lines(FOLDER_OF_TEXT_FILES)) {
    lines.flatMap(l -> Arrays.stream(l.split(" ")))
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

}

Нет необходимости сначала собирать все в список, это может быть сделано inline.
Также хорошо, что вы использовали try-with-resources.

0 голосов
/ 27 апреля 2020

можно использовать Pattern.splitAsStream, чтобы разделить строку быстрым способом и в то же время заменить все несловарные символы перед созданием карты счетчиков вхождений:

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*)+, где ищем необязательные несловесные символы, затем пробел, а затем снова необязательные несловесные символы.

0 голосов
/ 27 апреля 2020

Сначала для каждой строки мы удаляем все не-буквенные символы c (исключая пробелы), затем разделяем на пробелы, поэтому все элементы являются отдельными словами. Поскольку мы отображаем плоскую карту, поток состоит из всех слов. Затем мы просто собираем, используя 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());
0 голосов
/ 26 апреля 2020

Вместо применения 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);
    }
}

Вывод:

[Harry, is, a, good, cricketer, Tanya, is, an, intelligent, student, Bravo]

Примечание: Регулярное выражение \\s+ разбивает строку на пробелы.

0 голосов
/ 26 апреля 2020

Я могу неправильно понять ваш вопрос. Но если вам нужны слова, разделенные запятыми, попробуйте следующий код. Замените 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(",")) ;

Вы можете выполнять дальнейшие операции с последней строкой в ​​соответствии с вашими требованиями.

...