为什么在Java 8中split有时会在结果数组的开头删除空字符串?

Pshemo:

在Java 8之前,当我们分割空字符串时

String[] tokens = "abc".split("");

拆分机制将在标有的地方拆分 |

|a|b|c|

因为""每个字符前后都有空白因此,结果将首先生成此数组

["", "a", "b", "c", ""]

稍后将删除结尾的空字符串(因为我们没有为limit参数显式提供负值),因此它将最终返回

["", "a", "b", "c"]

在Java 8中,拆分机制似乎已更改。现在当我们使用

"abc".split("")

我们将获得["a", "b", "c"]数组而不是数组,["", "a", "b", "c"]因此看起来开始时的空字符串也被删除了。但是这个理论失败了,因为

"abc".split("a")

在start处返回带有空字符串的数组["", "bc"]

有人可以解释这里发生了什么,以及拆分规则在Java 8中是如何变化的吗?

nhahtdh:

String.split(调用Pattern.split的行为在Java 7和Java 8之间改变。

文献资料

Pattern.splitJava 7Java 8的文档之间进行比较,我们观察到添加了以下子句:

如果在输入序列的开头有一个正宽匹配,则在结果数组的开头将包含一个空的前导子字符串。开头的零宽度匹配永远不会产生这样的空前导子字符串。

Java 7相比String.splitJava 8中还添加了相同的子句

参考实施

让我们比较一下Pattern.splitJava 7和Java 8中的参考实现的代码。该代码是从grepcode检索的,用于版本7u40-b43和8-b132。

Java 7

public String[] split(CharSequence input, int limit) {
    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()) {
        if (!matchLimited || matchList.size() < limit - 1) {
            String match = input.subSequence(index, m.start()).toString();
            matchList.add(match);
            index = m.end();
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index,
                                             input.length()).toString();
            matchList.add(match);
            index = m.end();
        }
    }

    // If no match was found, return this
    if (index == 0)
        return new String[] {input.toString()};

    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());

    // Construct result
    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);
}

Java 8

public String[] split(CharSequence input, int limit) {
    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()) {
        if (!matchLimited || matchList.size() < limit - 1) {
            if (index == 0 && index == m.start() && m.start() == m.end()) {
                // no empty leading substring included for zero-width match
                // at the beginning of the input char sequence.
                continue;
            }
            String match = input.subSequence(index, m.start()).toString();
            matchList.add(match);
            index = m.end();
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index,
                                             input.length()).toString();
            matchList.add(match);
            index = m.end();
        }
    }

    // If no match was found, return this
    if (index == 0)
        return new String[] {input.toString()};

    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());

    // Construct result
    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);
}

在Java 8中,以下代码的添加排除了输入字符串开头的零长度匹配,这解释了上面的行为。

            if (index == 0 && index == m.start() && m.start() == m.end()) {
                // no empty leading substring included for zero-width match
                // at the beginning of the input char sequence.
                continue;
            }

保持兼容性

Java 8及更高版本中的以下行为

为了使make split在各个版本中表现一致,并与Java 8中的行为兼容:

  1. 如果您的正则表达式可以匹配零长度字符串,则只需(?!\A)正则表达式的末尾添加然后将原始正则表达式包装在非捕获组中即可(?:...)(如有必要)。
  2. 如果您的正则表达式不能匹配零长度字符串,则无需执行任何操作。
  3. 如果您不知道正则表达式是否可以匹配零长度字符串,请执行步骤1中的两个操作。

(?!\A) 检查字符串是否不以字符串开头结尾,这意味着匹配在字符串开头为空匹配。

Java 7及更高版本中的以下行为

没有通用的解决方案可以使它split与Java 7及更低版本向后兼容,除非替换所有实例split以指向您自己的自定义实现。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Java String.split()有时会给出空白字符串

为什么在split()结果中返回空字符串?

为什么使用'=='或'is'比较字符串有时会产生不同的结果?

为什么Java编译器有时会允许对null取消装箱?

为什么在Java中字符串以“”开头?

为什么在Java Servlet中的会话中设置属性有时会失败?

为什么没有从此Java流中过滤出空字符串?

为什么运行垃圾收集器有时会增加保留的ram java?

为什么Relation.size有时会在Rails 4中返回哈希

为什么打印char有时会在C中打印4个字节的数字

为什么我插入或删除Emacs有时会覆盖字符?

为什么农作物有时会在分类栅格上引入NA?

为什么close_fds = False有时会在Python 2中挂起进程?

为什么在python中使用re.split()时会得到那些空字符串?

为什么Eclipse CDT代码格式化程序有时会在模板参数中引入空格?

为什么PyQt有时会在退出时崩溃?

为什么使用[与[[]有时会产生相同的结果(而不是不同的结果)?

为什么浏览器有时会在进入HTTPS页面时要求确认?

为什么有时会在终端中反复提示“>”?

为什么读取属性有时会在javascript中引发错误?

为什么Java String.split()留在空字符串后面?

人们为什么有时会声明数组大小?

为什么有时会看到Java包名称与文件夹结构不同?

为什么 C# 单例类有时会在多线程中调用两个方法?

为什么 index 有时会在 reduce() 方法中意外运行?

为什么 next() 有时会在生成器上隐式调用?

为什么我的 Ubuntu 20.04 安装有时会在启动时挂起?

为什么java在解析这个XML时会保存空字符串?

为什么我的 VBA 程序有时会在调试模式下停止?