前言
版本约定
- 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 类中使用的 split 方法。
丢失结尾空字符串
先猜一下如下代码的执行结果是多少,是不是和你预期的一样?
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 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
