Java字符串分割

Posted 刘润森!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java字符串分割相关的知识,希望对你有一定的参考价值。

前言

本章介绍字符串分割的使用案例,同时会给出一些使用建议。
版本约定

  • 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 方法不是通过正则表达式的方式实现字符串分割的,所以我们也不用考虑正则表达式的特殊字符转义的问题了

丢失结尾空字符串

先猜一下如下代码的执行结果是多少,是不是和你预期的一样?

public static void main(String[] args) 
    String str = "1||3|";
    String[] arr = str.split("\\\\|");
    System.out.println(arr.length);

行程序,输出:

3

根据字符 | 分割的话,我想上面应该能分成 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 的重载方法,在使用的时候根据自己的需要选择。

以上是关于Java字符串分割的主要内容,如果未能解决你的问题,请参考以下文章

什么是正则表达式?

java使用正则表达式分割字符串,已有千人收藏

JAVA 一个或多个空格分割字符串

分享一个 Java String split 快速分割的方法

PHP系统PHP正则表达式

Java正则表达式基本应用