У меня есть список таких строк, как «Такси или водитель автобуса». Мне нужно преобразовать первую букву каждого слова в заглавную, кроме слова «или». Есть ли простой способ добиться этого с помощью потока Java. Я пробовал с техникой Pattern.compile.splitasstream, я не мог объединить все разделенные токены обратно, чтобы сформировать исходную строку Любая помощь будет принята с благодарностью. Если кому-то понадобится, я могу разместить здесь свой код.




Вот мой код:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ConvertToCapitalUsingStreams {
// collection holds all the words that are not to be capitalized
private static final List<String> EXCLUSION_LIST = Arrays.asList(new String[]{"or"});
public String convertToInitCase(final String data) {
String[] words = data.split("\\s+");
List<String> initUpperWords = Arrays.stream(words).map(word -> {
//first make it lowercase
return word.toLowerCase();
}).map(word -> {
//if word present in EXCLUSION_LIST return the words as is
if (EXCLUSION_LIST.contains(word)) {
return word;
}
//if the word not present in EXCLUSION_LIST, Change the case of
//first letter of the word and return
return Character.toUpperCase(word.charAt(0)) + word.substring(1);
}).collect(Collectors.toList());
// convert back the list of words into a single string
String finalWord = String.join(" ", initUpperWords);
return finalWord;
}
public static void main(String[] a) {
System.out.println(new ConvertToCapitalUsingStreams().convertToInitCase("Taxi or bus driver"));
}
}
Примечание: Вы также можете посмотреть этот пост ТАК об использовании библиотеки apache commons-text для выполнения этой работы.
Разделите строку на слова, затем преобразуйте первый символ в верхний регистр, затем joining, чтобы сформировать исходную строку:
String input = "Taxi or bus driver";
String output = Stream.of(input.split(" "))
.map(w -> {
if (w.equals("or") || w.length() == 0) {
return w;
}
return w.substring(1) + Character.toUpperCase(w.charAt(0));
})
.collect(Collectors.joining(" "));
А твоя причина?
вы делаете ненужный вызов Stream#of, который внутренне вызывает Arrays#stream
@Eugene Я бы предпочел Arrays.stream по семантическим причинам. String.split возвращает массив, а Arrays.stream - правильная идиома для потоковой передачи по массиву. Напротив, Stream.of(…) - это метод varargs, который может принимать массивы из-за того, как были реализованы varargs (и для совместимости с кодом до Java 5).
Вам нужен правильный шаблон, чтобы идентифицировать место расположения, в котором необходимо внести изменение, шаблон нулевой ширины, когда вы хотите использовать splitAsStream. Соответствие местоположению, которое
Объявите это как
static final Pattern WORD_START_BUT_NOT_OR = Pattern.compile("\\b(?=\\p{Ll})(?!or\\b)");
Затем использовать его для обработки токенов просто с потоком и map. Возврат строки работает через .collect(Collectors.joining()):
List<String> input = Arrays.asList("Taxi or bus driver", "apples or oranges");
List<String> result = input.stream()
.map(s -> WORD_START_BUT_NOT_OR.splitAsStream(s)
.map(w -> Character.toUpperCase(w.charAt(0))+w.substring(1))
.collect(Collectors.joining()))
.collect(Collectors.toList());
result.forEach(System.out::println);
Taxi or Bus Driver
Apples or Oranges
Обратите внимание, что при разделении всегда будет первый токен, независимо от того, соответствует ли он критериям. Поскольку слово «или» обычно никогда не появляется в начале фразы, а преобразование прозрачно для символов, отличных от строчных букв, здесь это не должно быть проблемой. В противном случае специальная обработка первого элемента потоком сделает код слишком сложным. Если это проблема, предпочтительнее использовать петлю.
Решение на основе цикла может выглядеть как
private static final Pattern FIRST_WORD_CHAR_BUT_NOT_OR
= Pattern.compile("\\b(?!or\\b)\\p{Ll}");
(теперь используется шаблон, который соответствует персонажу, а не смотрит на него)
public static String capitalizeWords(String phrase) {
Matcher m = FIRST_WORD_CHAR_BUT_NOT_OR.matcher(phrase);
if (!m.find()) return phrase;
StringBuffer sb = new StringBuffer();
do m.appendReplacement(sb, m.group().toUpperCase()); while(m.find());
return m.appendTail(sb).toString();
}
который, в качестве бонуса, также может обрабатывать символы, охватывающие несколько устройств char. Начиная с Java 9, StringBuffer можно заменить на StringBuilder для повышения эффективности. Этот метод можно использовать как
List<String> result = input.stream()
.map(s -> capitalizeWords(s))
.collect(Collectors.toList());
Также возможна замена лямбда-выражения s -> capitalizeWords(s) ссылкой на метод в форме ContainingClass::capitalizeWords.
черт возьми, вот-вот отправится с шаблоном, близким к этому: |
Привет, #Holger, первая часть решения работает не так, как ожидалось, она не преобразуется в верхний регистр.
Или с java9 Matcher.replaceAll, FIRST_WORD_CHAR_BUT_NOT_OR.matcher(phrase).replaceAll(mr->mr.group().toUpperCase())
@PhilipPuthenvila была опечатка в регулярном выражении, он искал прописные буквы вместо строчных. Я исправил это. Спасибо.
Это решение подходит для моих требований