Если бы нам нужно было только подсчитать специальные символы и гласные, мы могли бы использовать что-то вроде этого:
Map<String,Long> result;
try(Stream<String> lines = Files.lines(path)) {
result = lines
.flatMap(Pattern.compile("\\s+")::splitAsStream)
.flatMapToInt(String::chars)
.filter(c -> !Character.isAlphabetic(c) || "aeiou".indexOf(c) >= 0)
.mapToObj(c -> "aeiou".indexOf(c)>=0? "totalVowelCount": "totalSpecialCharacter")
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
Сначала мы сливаем поток строк в поток слов, затем в поток символов,сгруппировать их по типу.Это работает гладко, так как «специальный символ» и «гласный» являются взаимоисключающими.В принципе, уплощение к словам могло бы быть опущено, если бы мы просто расширили фильтр, чтобы пропустить символы пробела, но здесь это помогает получить решение для подсчета слов.
Поскольку слова - это другой тип сущностичем символы, подсчет их в одной и той же операции не так прост.Одно из решений состоит в том, чтобы ввести псевдосимвол для каждого слова и посчитать его, как и другие символы в конце.Поскольку все действительные символы положительные, мы можем использовать -1
для этого:
Map<String,Long> result;
try(Stream<String> lines = Files.lines(path)) {
result = lines.flatMap(Pattern.compile("\\s+")::splitAsStream)
.flatMapToInt(w -> IntStream.concat(IntStream.of(-1), w.chars()))
.mapToObj(c -> c==-1? "totalWordCount": "aeiou".indexOf(c)>=0? "totalVowelCount":
Character.isAlphabetic(c)? "totalAlphabetic": "totalSpecialCharacter")
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
Это добавляет категорию "totalAlphabetic"
в дополнение к другим в карту результатов.Если вы не хотите этого, вы можете вставить шаг .filter(cat -> !cat.equals("totalAlphabetic"))
между шагами mapToObj
и collect
.Или используйте фильтр, как в первом решении до шага mapToObj
.
В качестве дополнительного примечания, это решение выполняет больше работы, чем необходимо, потому что оно разбивает входные данные на строки, что не является необходимым, как мы можемтрактовать разрывы строк так же, как и другие пробелы, то есть как границу слова.Начиная с Java 9, мы можем использовать Scanner
для задания:
Map<String,Long> result;
try(Scanner scanner = new Scanner(path)) {
result = scanner.findAll("\\S+")
.flatMapToInt(w -> IntStream.concat(IntStream.of(-1), w.group().chars()))
.mapToObj(c -> c==-1? "totalWordCount": "aeiou".indexOf(c)>=0? "totalVowelCount":
Character.isAlphabetic(c)? "totalAlphabetic": "totalSpecialCharacter")
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
Это разделит ввод на слова в первую очередь без особой обработки переносов строк. Этот ответ содержит совместимую с Java 8 реализацию Scanner.findAll
.
Приведенные выше решения рассматривают каждый символ, который не является ни пробелом, ни алфавитом, как «специальный символ».Если ваше определение «специального символа» отличается, адаптировать решения не должно быть слишком сложно.