String.split(String regex, int limit)方法剖析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Miaoshuowen/article/details/101556738

1.split源码分析

split方法准确的来说有两个参数(String regex, int limit),只不过平时我们用的,是split的一个重载方法(String regex),默认是把第二个参数设置为0,源码如下:

public String[] split(String regex) {
    return split(regex, 0);
}

   public String[] split(String regex, int limit) {
        /* fastpath if the regex is a
         (1)one-char String and this character is not one of the
            RegEx's meta characters ".$|()[{^?*+\\", or
         (2)two-char String and the first char is the backslash and
            the second is not the ascii digit or ascii letter.
         */
        char ch = 0;
        if (((regex.value.length == 1 &&
             ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
             (regex.length() == 2 &&
              regex.charAt(0) == '\\' &&
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
              ((ch-'a')|('z'-ch)) < 0 &&
              ((ch-'A')|('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))
        {
            int off = 0;
            int next = 0;
            boolean limited = limit > 0;
            ArrayList<String> list = new ArrayList<>();
            while ((next = indexOf(ch, off)) != -1) {
                if (!limited || list.size() < limit - 1) {
                    list.add(substring(off, next));
                    off = next + 1;
                } else {    // last one
                    //assert (list.size() == limit - 1);
                    list.add(substring(off, value.length));
                    off = value.length;
                    break;
                }
            }
            // If no match was found, return this
            if (off == 0)
                return new String[]{this};

            // Add remaining segment
            if (!limited || list.size() < limit)
                list.add(substring(off, value.length));

            // Construct result
            int resultSize = list.size();
            if (limit == 0) {
                while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }

字符串分割

第一次分割时,使用off和next,off指向每次分割的起始位置,next指向分隔符的下标,完成一次分割后更新off的值,当list的大小等于limit-1时,直接添加剩下子字符串。

如果字符串不含分隔符,直接返回原字符串。
如果字符串第一次分割完后没有数量没有达到limit-1,最终余下的字符串在第二次被添加。
在limit等于0的情况下,从最后一个子字符串往前数,所有的空字符串""都会被清除。

根据匹配给定的正则表达式来拆分此字符串。
此方法返回的数组包含此字符串的子字符串,每个子字符串都由另一个匹配给定表达式的子字符串终止,或者由此字符串末尾终止。数组中的子字符串按它们在此字符串中出现的顺序排列。如果表达式不匹配输入的任何部分,那么所得数组只具有一个元素,即此字符串。

limit 参数控制模式应用的次数,因此影响所得数组的长度。如果该限制 n 大于 0,则模式将被最多应用 n - 1 次,数组的长度将不会大于 n,而且数组的最后一项将包含所有超出最后匹配的定界符的输入。如果 n 为非正,那么模式将被应用尽可能多的次数,而且数组可以是任何长度。如果 n 为 0,那么模式将被应用尽可能多的次数,数组可以是任何长度,并且结尾空字符串将被丢弃。

例如,字符串 “boo:and:foo” 使用这些参数可生成以下结果:

Regex Limit 结果
2 { “boo”, “and:foo” }
5 { “boo”, “and”, “foo” }
-2 { “boo”, “and”, “foo” }
o 5 { “b”, “”, “:and:f”, “”, “” }
o -2 { “b”, “”, “:and:f”, “”, “” }
o 0 { “b”, “”, “:and:f” }

调用此方法的 str.split(regex, n) 形式与以下表达式产生的结果完全相同:

Pattern.compile(regex).split(str, n)

参数
regex - 定界正则表达式
limit - 结果阈值,如上所述
返回:
字符串数组,它是根据给定正则表达式的匹配拆分此字符串确定的
抛出:
PatternSyntaxException - 如果正则表达式的语法无效

public String[] split(String regex)根据给定正则表达式的匹配拆分此字符串。
该方法的作用就像是使用给定的表达式和限制参数 0 来调用两参数 split 方法。因此,所得数组中不包括结尾空字符串。

例如,字符串 “boo:and:foo” 使用这些表达式可生成以下结果:

扫描二维码关注公众号,回复: 7643112 查看本文章
Regex 结果
{ “boo”, “and”, “foo” }
o { “b”, “”, “:and:f” }

参数
regex - 定界正则表达式
返回:
字符串数组,它是根据给定正则表达式的匹配拆分此字符串确定的
抛出:
PatternSyntaxException - 如果正则表达式的语法无效

特殊情况判断:

if (((regex.length() == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
             (regex.length() == 2 && regex.charAt(0) == '\\' &&
              (((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
              ((ch-'a')|('z'-ch)) < 0 &&
              ((ch-'A')|('Z'-ch)) < 0)) &&
            (ch < Character.MIN_HIGH_SURROGATE ||
             ch > Character.MAX_LOW_SURROGATE))

regex长度为1时,将regex赋给ch,判断ch是否在.$|()[{^?*+\中;
regex长度为2时,第一个字符为\(要表示一个\需要用两个\转义得到),第二个字符不在0-9,a-z,A-Z中,且不在Unicode编码的\uD800-\uDBFF之间。\uD800-\uDBFF区间表示的是UTF-16中的低代理项,具体代表字符可以去编码表自行查看。源码如下:

 /**
     * The minimum value of a
     * <a href="http://www.unicode.org/glossary/#high_surrogate_code_unit">
     * Unicode high-surrogate code unit</a>
     * in the UTF-16 encoding, constant {@code '\u005CuD800'}.
     * A high-surrogate is also known as a <i>leading-surrogate</i>.
     *
     * @since 1.5
     */
    public static final char MIN_HIGH_SURROGATE = '\uD800';
    public static final char MAX_HIGH_SURROGATE = '\uDBFF';

2不同limit值的情况下的split结果验证

limit=0,regex=","
尾部的逗号,直接被忽略,头部的逗号不会忽略

String line = ",aa,bcd,eef,,,";
String[] split = line.split(",",0);
System.out.println(split.length);//4

limit=2,regex=","
总长度被限制成最大2个

String line = ",aa,bcd,eef,,,";
String[] split = line.split(",",2);
System.out.println(split.length);//2

limit=100,regex=","
总长度被限制成最大100个
但结果是7个,说明当limit大于0,并且远大于应该有的长度时,头部和尾部的逗号都没有被忽略

String line = ",aa,bcd,eef,,,";
String[] split = line.split(",",100);
System.out.println(split.length);//7

limit=-1,regex=","
结果是7个,说明当limit小于0时,头部和尾部的逗号都没有被忽略

String line = ",aa,bcd,eef,,,";
String[] split = line.split(",",-1);
System.out.println(split.length);//7

猜你喜欢

转载自blog.csdn.net/Miaoshuowen/article/details/101556738
今日推荐