您需要正确的模式来标识必须进行更改的位置,当您想使用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());
也可以将 lambda 表达式 s -> capitalizeWords(s) 替换为 ContainingClass::capitalizeWords 形式的方法引用。