zoukankan      html  css  js  c++  java
  • Java字符串分割函数split源码分析

    spilt方法作用

    以所有匹配regex的子串为分隔符,将input划分为多个子串。

    例如:

    The input "boo:and:foo", for example, yields the following results with these expressions:
    Regex Result
    :{ "boo", "and", "foo" }
    o { "b", "", ":and:f" }

    limit参数

    ①、limit > 0 ,则pattern(模式)应用limit - 1 次

    ②、limit = 0 ,则pattern(模式)应用无限次并且省略末尾的空子串

    ③、limit < 0 ,则pattern(模式)应用无限次,不省略空子串

    如果没有传limit参数,那么实际上调用的是split(regex,0),即limit=0,此时尽可能地分割并省略尾部的空子串。

    String.split(String regex, int limit)源码

    参数regex是String类型,但需要按照正则表达式的语法格式去写(例如有的字符在正则表达式里有特殊含义而我们不想它有这特殊含义,那么我们就要对这个字符进行转义)。为什么要按照正则表达式的语法格式去写呢?因为我们实际上需要借助Pattern类的split方法来完成功能。

    但如果regex比较简单,杀鸡焉用牛刀,我们就自己实现分割而不用去调用Pattern.split方法。当regex属于特定两种类型之一时是自己去实现分割的:

    (1)regex只包含单个字符且不是特殊字符。

    (2)regex包含两个字符,第一个字符是正则表达式转义用的反斜杠,即regex是个转义字符,但转义前后只是改变了字符的语义没有改变字符的外形。例如"\?"直接就表示"?"这个字符,前面加上反斜杠是因为"?"在正则表达式中为特殊字符,我们需要转义(之所以有两个反斜杠是因为反斜杠本身在Java字符串中就有转义功能,所以需要转义一次)。但实际上我们没有交由Pattern.split方法来处理,也就是说我们根本没有涉及到正则表达式匹配,那么这里的转义就没有意义了,那么我们就可以直接取"\?"的第二个字符"?"作为分隔符。

    无论上面两种情况的哪一种,最后实际上都是单个字符作为分隔符的。而对于更复杂的情况,我们去调用Pattern.split方法。

    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))
        {
            //即将加入list的子串的起始索引
            int off = 0;
            //分隔符下次出现的索引
            int next = 0;
            boolean limited = limit > 0;
            //结果集
            ArrayList<String> list = new ArrayList<>();
            //ch为分隔符
            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).isEmpty()) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }
    

    Pattern.split(CharSequence input, int limit)源码

    /**
    ①、limit > 0 ,则pattern(模式)应用limit - 1 次
    
    ②、limit = 0 ,则pattern(模式)应用无限次并且省略末尾的空字串
    
    ③、limit < 0 ,则pattern(模式)应用无限次,不省略空字符串
    */
    public String[] split(CharSequence input, int limit) {
        //即将加入matchList的子串的起始索引
        int index = 0;
        boolean matchLimited = limit > 0;
        //结果集
        ArrayList<String> matchList = new ArrayList<>();
        Matcher m = matcher(input);
    
        // Add segments before each match found
        while(m.find()) {
            //当limit>0时将input划分为limit段
            //当!matchLimited为true即limit<=0时划分次数不受限制
            if (!matchLimited || matchList.size() < limit - 1) {
                //如果分隔符为空字符串需要保证结果序列的第一个字符串不为空
                //例如"hello"应该被分割为'h','e','l', 'l', 'o',而不是'',h','e','l', 'l', 'o'
                if (index == 0 && index == m.start() && m.start() == m.end()) {
                    continue;
                }
                String match = input.subSequence(index, m.start()).toString();
                matchList.add(match);
                index = m.end();
            } else if (matchList.size() == limit - 1) { // 不用继续分割,剩余所有字符作为最后一个子串
                String match = input.subSequence(index,
                                                 input.length()).toString();
                matchList.add(match);
                index = m.end();
            }
        }
    
        // 没有子串匹配,返回原字符串
        if (index == 0)
            return new String[] {input.toString()};
    
        // 剩余所有字符作为最后一个子串
        if (!matchLimited || matchList.size() < limit)
            matchList.add(input.subSequence(index, input.length()).toString());
    
        int resultSize = matchList.size();
        //删除尾部所有空子串
        if (limit == 0)
            while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
                resultSize--;
        String[] result = new String[resultSize];
        return matchList.subList(0, resultSize).toArray(result);
    }
    
  • 相关阅读:
    sql:除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询
    [转]sql:除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询
    [转]IIS6 伪静态 IIS文件类型映射配置方法 【图解】
    IIS6 伪静态 IIS文件类型映射配置方法 【图解】
    [转]正则表达式的多行模式与单行模式
    正则表达式的多行模式与单行模式
    [原]MS SQL表字段自增相关的脚本
    MS SQL表字段自增相关的脚本
    angular学习笔记(六)-非入侵式javascript
    angular学习笔记(五)-阶乘计算实例(3)
  • 原文地址:https://www.cnblogs.com/Frank-Hong/p/15376486.html
Copyright © 2011-2022 走看看