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

2024-01-12

我正在制作一个基于文本的骰子滚筒。它接受像“2d10+5”这样的字符串,并返回一个字符串作为掷骰的结果。我的问题出现在分词器中,它将字符串分割成有用的部分,以便我解析成信息。

String[] tokens = message.split("(?=[dk\\+\\-])");

这产生了奇怪的、意想不到的结果。我不知道到底是什么原因造成的。这可能是正则表达式、我的误解,或者 Java 就是 Java。这是发生的事情:

  • 3d6+4产生字符串数组[3, d6, +4]。这是对的。
  • d%产生字符串数组[d%]。这是对的。
  • d20产生字符串数组[d20]。这是对的。
  • d%+3产生字符串数组[, d%, +3]。这是不正确的。
  • d20+2产生字符串数组[, d20, +2]。这是不正确的。

在第四个和第五个示例中,一些奇怪的事情导致数组的前面出现了额外的空字符串。这并不是字符串前面缺少数字,其他例子反驳了这一点。这不是百分号或加号的存在。

现在我只是继续对空白字符串进行 for 循环,但这感觉有点像创可贴解决方案。有谁知道是什么原因导致数组前面出现空白字符串?我该如何修复它?


通过深入研究源代码,我得到了这种行为背后的确切问题。

The String.split()方法内部使用Pattern.split()。 split 方法在返回结果数组之前检查最后一个匹配的索引或是否确实存在匹配。如果最后一个匹配的索引是0,这意味着您的模式仅匹配字符串开头的空字符串或根本不匹配,在这种情况下,返回的数组是包含相同元素的单元素数组。

这是源代码:

public String[] split(CharSequence input, int limit) {
        int index = 0;
        boolean matchLimited = limit > 0;
        ArrayList<String> matchList = new ArrayList<String>();
        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);

                // Consider this assignment. For a single empty string match
                // m.end() will be 0, and hence index will also be 0
                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()};

        // Rest of them is not required

如果上面代码中的最后一个条件 -index == 0,为 true,则单个元素数组与输入字符串一起返回。

现在,考虑以下情况:index can be 0.

  1. 当根本没有匹配的时候。 (正如上面的评论中已经提到的那样)
  2. 如果在开头找到匹配项,则匹配字符串的长度为0,那么索引中的值if块(在while环形) -

    index = m.end();
    

    将为 0。唯一可能的匹配字符串是空字符串(长度=0)。这里的情况正是如此。而且也不应该有任何进一步的比赛,否则index将更新为不同的索引。

因此,考虑到您的情况:

  • For d%,在第一个之前只有一个模式匹配d。因此索引值将是0。但由于没有任何进一步的匹配,索引值不会更新,并且if条件变为true,并返回带有原始字符串的单元素数组。

  • For d20+2将会有两场比赛,一场之前d,以及之前的一个+。因此索引值将被更新,因此ArrayList上面代码中的 将会被返回,其中包含由于分隔符(字符串的第一个字符)分割而导致的空字符串,正如 @Stema 的答案中已经解释的那样。

因此,为了获得您想要的行为(仅当分隔符不在开头时才在分隔符上拆分,您可以在正则表达式模式中添加负后视):

"(?<!^)(?=[dk+-])"  // You don't need to escape + and hyphen(when at the end)

这将在空字符串上分割,后跟您的字符类,但前面不包含字符串的开头。


考虑分割字符串的情况"ad%"在正则表达式模式上 -"a(?=[dk+-])"。这将为您提供一个第一个元素为空字符串的数组。这里唯一的变化是,空字符串被替换为a:

"ad%".split("a(?=[dk+-])");  // Prints - `[, d%]`

为什么?这是因为匹配字符串的长度是1。所以第一次匹配后的索引值 -m.end()不会是0 but 1,因此不会返回单元素数组。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java String.split() 有时会给出空白字符串 的相关文章

随机推荐