我正在寻找一种在单词和非单词中分割字符串的优雅方式,其中“单词”由一些正则表达式定义(例如,[a-zA-Z]+
)。
输入是一个字符串,输出应该是按顺序排列的单词和非单词子串。例如:
"A! B C, d." -> Arrays.asList("A", "! ", "B", " ", "C", ", ","d", ".")
这是我的看法:
public static String WORD_PATTERN = "[a-zA-Z]+";
public static List<String> splitString(String str) {
if (str == null) {
return null;
}
Pattern wordPattern = Pattern.compile(WORD_PATTERN);
Matcher wordMatcher = wordPattern.matcher(str);
List<String> splitString = new ArrayList<>();
int endOfLastWord = 0;
while(wordMatcher.find())
{
int startOfNextWord = wordMatcher.start();
int endOfNextWord = wordMatcher.end();
if (startOfNextWord > endOfLastWord) {
String nextNonWord = str.substring(endOfLastWord, startOfNextWord);
splitString.add(nextNonWord);
}
String nextWord = str.substring(startOfNextWord, endOfNextWord);
splitString.add(nextWord);
endOfLastWord = endOfNextWord;
}
if (endOfLastWord < str.length()) {
String lastNonWord = str.substring(endOfLastWord);
splitString.add(lastNonWord);
}
return splitString;
}
这感觉不太优雅,我认为应该有一种更好的方法,我只是不知道。
我不打算改进上面的代码,所以请不要参考Codereview。我只发布它以避免“你到目前为止尝试了什么”评论。
我正在寻找一种更简洁,更优雅的方式,理想情况下只使用标准的Java包。
答案 0 :(得分:2)
您可以使用正则表达式捕获包含可选内容的单词和非单词:
(\w*)(\W*)
对于每个匹配,取两个捕获组,检查是否捕获了值(长度> 0)并将值添加到列表中。
这给出了一个简单明了的解决方案,如:
public List<String> splitWord(String s){
List<String> result = new ArrayList<>();
Pattern p = Pattern.compile("(\\w*)(\\W*)");
Matcher m = p.matcher(s);
while(m.find()){
Optional.of(m.group(1)).filter(str -> !str.isEmpty()).ifPresent(result::add);
Optional.of(m.group(2)).filter(str -> !str.isEmpty()).ifPresent(result::add);
}
return result;
}
注意:Optional
是......可选的,但我正在尝试改进自己。它只会检查该组是否具有非空值,并将其添加到列表中。
结果格式化以匹配您的示例
"abc def" -> Arrays.asList("abc", " ", "def")
"a.b. c" -> Arrays.asList("a", ".", "b", ". ", "c")
"a.b." -> Arrays.asList("a", ".", "b", ".")
".aa" -> Arrays.asList(".", "aa")
"." -> Arrays.asList(".")
"a" -> Arrays.asList("a")
".." -> Arrays.asList("..")
以下是ideone
中格式化方法的示例