前言
版本约定
- JDK 版本:1.8.0_231
- Java SE API Documentation:https://docs.oracle.com/javase/8/docs/api/
正文
字符串分割,需要注意两个问题:特殊字符和丢失结尾空字符串,我们一个一个来分析。
特殊字符
字符串分割,一般会使用 String 类的 split
方法,比如我需要按照逗号分割字符串。
public static void main(String[] args) {
String str = "1,2,3,4";
String[] arr = str.split(",");
System.out.println(Arrays.toString(arr));
}
运行程序,输出:
[1, 2, 3, 4]
但是,如果我想通过特殊符号 | 分割字符串呢?
public static void main(String[] args) {
String str = "1|2|3|4";
String[] arr = str.split("|");
System.out.println(Arrays.toString(arr));
}
运行程序,输出:
[1, |, 2, |, 3, |, 4]
悲剧的发现,执行结果并不是我们期望的结果,这是为什呢?该如何处理呢?
我们先来看下 String 类中 split
方法的注释和源码。
/**
* Splits this string around matches of the given <a
* href="../util/regex/Pattern.html#sum">regular expression</a>.
*
* <p> This method works as if by invoking the two-argument {@link
* #split(String, int) split} method with the given expression and a limit
* argument of zero. Trailing empty strings are therefore not included in
* the resulting array.
*
* <p> The string {@code "boo:and:foo"}, for example, yields the following
* results with these expressions:
*
* <blockquote><table cellpadding=1 cellspacing=0 summary="Split examples showin
* <tr>
* <th>Regex</th>
* <th>Result</th>
* </tr>
* <tr><td align=center>:</td>
* <td>{@code { "boo", "and", "foo" }}</td></tr>
* <tr><td align=center>o</td>
* <td>{@code { "b", "", ":and:f" }}</td></tr>
* </table></blockquote>
*
*
* @param regex
* the delimiting regular expression
*
* @return the array of strings computed by splitting this string
* around matches of the given regular expression
*
* @throws PatternSyntaxException
* if the regular expression's syntax is invalid
*
* @see java.util.regex.Pattern
*
* @since 1.4
* @spec JSR-51
*/
public String[] split(String regex) {
return split(regex, 0);
}
传入 split 方法的分割参数其实代表的是一个正则表达式,它是通过正则表达式来实现字符串分割的。
正则表达式中有些字符具有特殊的含义,如果在匹配中要用到它本来的含义,需要进行转义(在其前面加一个 \),具体哪些字符需要转移,会另外写一篇文章描述。
不得已,我们需要先对分隔符中的正则表达式特殊字符进行转义,才能传入 split
方法。
public static void main(String[] args) {
String str = "1|2|3|4";
String[] arr = str.split("\\|");
System.out.println(Arrays.toString(arr));
}
运行程序,输出:
[1, 2, 3, 4]
这样才能得出正确的结果。
上面讲字符串比较的时候,最后使用了 StringUtils 工具类的中的方法,那字符串分割,它有提供相应的方法么?
答案是肯定的,貌似重载的方法还挺多,功能丰富,先使用一下,看看效果。
public static void main(String[] args) {
String str = "1|2|3|4";
String[] arr = StringUtils.split(str, "|");
System.out.println(Arrays.toString(arr));
}
运行程序,输出:
[1, 2, 3, 4]
直接结果正确,而且我也没考虑特殊字符的情况,因为 StringUtils 的 split
方法不是通过正则表达式的方式实现字符串分割的,所以我们也不用考虑正则表达式的特殊字符转义的问题了。
同样的,关于字符串分割也是推荐使用 StringUtils 类中使用的 spli
t 方法。
丢失结尾空字符串
先猜一下如下代码的执行结果是多少,是不是和你预期的一样?
public static void main(String[] args) {
String str = "1||3|";
String[] arr = str.split("\\|");
System.out.println(arr.length);
}
运行程序,输出:
3
(⊙o⊙)…,根据字符 | 分割的话,我想上面应该能分成 4 断,那执行结果应该 4,为什么这里输出了 3 呢?
查看 String 类的 split 方法,发现它还调用了另外一个重载方法。
public String[] split(String regex) {
return split(regex, 0);
}
public String[] split(String regex, int limit) {
......
}
limit 参数表示什么含义呢?根据注释描述,我们知道 limit 参数用来控制模式应用的次数,因此它会影响所得数组的长度。
- limit 大于 0:则模式将被最多应用 n-1 次,数组的长度将不会大于 n,而且数组的最后一项将包含所有超出最后匹配的定界符的输入。
- limit 等于 0:则模式将被应用尽可能多的次数,数组可以是任何长度,并且结尾空字符串将被丢弃。
- limit 小于 0:则模式将被应用尽可能多的次数,数组可以是任何长度。
而 split(String regex)
方法默认传的 limit 值是 0,那就是说执行结果会丢弃结尾的空字符串,刚好上面的例子中的字符串根据字符 | 分割后,结尾就是一个空字符串,所以执行结果是 3。
根据上面的描述,我们如果想保留结尾的空字符串的话,需要调用 split(String regex, int limit)
方法。
public static void main(String[] args) {
String str = "1||3|";
String[] arr = str.split("\\|", -1);
System.out.println(arr.length);
}
运行程序,输出:
4
这样才能达到预期的结果。
我们使用 StringUtils 工具类中的 split
方法试试呢。
public static void main(String[] args) {
String str = "1||3|";
String[] arr = StringUtils.split(str, "|");
System.out.println(arr.length);
}
运行程序,输出:
2
这个更狠,把空字符串都丢掉了,所以我们平时在写代码的时候,还是需要写 Unit Test 的,需要把可能的场景列出来,通过 Unit Test 覆盖测试一遍,才能知道我们的代码是否健壮,能否达到预期的结果。
StringUtils 工具类中 split
相关的源码比较多,这里就不一一列出来了,大家直接进到该类中,看注释就能找到你需要执行效果的方法。
比如这里我想包含空字符串,不想过滤空字符串的话,可以使用 StringUtils 类中的 splitByWholeSeparatorPreserveAllTokens
方法。
public static void main(String[] args) {
String str = "1||3|";
String[] arr = StringUtils.splitByWholeSeparatorPreserveAllTokens(str, "|");
System.out.println(arr.length);
}
运行程序,输出:
4
总结
字符串的分割推荐使用 StringUtils 工具类中的 split
方法,如果不想丢失空字符串,推荐使用 StringUtils 工具类中的 splitByWholeSeparatorPreserveAllTokens
方法。
该工具类中有很多 split
的重载方法,在使用的时候根据自己的需要选择。
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/yrq1di 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。