java 正则表达式中捕获组的行为混乱

2023-11-22

In this answer我推荐使用

s.replaceFirst("\\.0*$|(\\.\\d*?)0+$", "$1");

但有两个人抱怨结果包含字符串“null”,例如,23.null。这可以解释为$1 (i.e., group(1)) being null,可以通过以下方式进行转换String.valueOf到字符串“null”。但是,我总是得到空字符串。我的testcase覆盖它并且

assertEquals("23", removeTrailingZeros("23.00"));

通过。确切的行为是否未定义?


的文档Matcher参考实现中的类没有指定的行为appendReplacement当捕获组不捕获任何内容时的方法(null) 在替换字符串中指定。虽然行为group方法很清楚,没有提及appendReplacement method.

以下是上述案例实施差异的 3 个表现:

  • 对于上述情况,参考实现不会附加任何内容(或者我们可以说附加一个空字符串)。
  • GNU Classpath 和 Android 的实现附加null对于上述情况。

为了简洁起见,省略了一些代码,并由....

1) Sun/Oracle JDK、OpenJDK(参考实现)

对于参考实现(Sun/Oracle JDK 和 OpenJDK),代码为appendReplacement从 Java 6 开始似乎没有改变,当捕获组没有捕获任何内容时,它不会附加任何内容:

        } else if (nextChar == '$') {
            // Skip past $
            cursor++;
            // The first number is always a group
            int refNum = (int)replacement.charAt(cursor) - '0';
            if ((refNum < 0)||(refNum > 9))
                throw new IllegalArgumentException(
                    "Illegal group reference");
            cursor++;

            // Capture the largest legal group string
            ...

            // Append group
            if (start(refNum) != -1 && end(refNum) != -1)
                result.append(text, start(refNum), end(refNum));
        } else {

参考

  • jdk6/98e143b44620
  • jdk8/687fd7c7986d

2) GNU 类路径

GNU Classpath 是 Java 类库的完全重新实现,对于以下方面有不同的实现:appendReplacement在上述情况下。在 Classpath 中,类位于java.util.regex类路径中的包只是类的包装器gnu.java.util.regex.

Matcher.appendReplacement calls RE.getReplacement处理匹配部分的替换:

  public Matcher appendReplacement (StringBuffer sb, String replacement)
    throws IllegalStateException
  {
    assertMatchOp();
    sb.append(input.subSequence(appendPosition,
                                match.getStartIndex()).toString());
    sb.append(RE.getReplacement(replacement, match,
        RE.REG_REPLACE_USE_BACKSLASHESCAPE));
    appendPosition = match.getEndIndex();
    return this;
  }

RE.getReplacement calls REMatch.substituteInto获取捕获组的内容并直接附加其结果:

                  case '$':
                    int i1 = i + 1;
                    while (i1 < replace.length () &&
                           Character.isDigit (replace.charAt (i1)))
                      i1++;
                    sb.append (m.substituteInto (replace.substring (i, i1)));
                    i = i1 - 1;
                    break;

REMatch.substituteInto附加结果REMatch.toString(int)直接不检查捕获组是否捕获了任何内容:

        if ((input.charAt (pos) == '$')
            && (Character.isDigit (input.charAt (pos + 1))))
          {
            // Omitted code parses the group number into val
            ...

            if (val < start.length)
              {
                output.append (toString (val));
              }
          }

And REMatch.toString(int)回报null当捕获组不捕获时(不相关的代码已被省略)。

  public String toString (int sub)
  {
    if ((sub >= start.length) || sub < 0)
      throw new IndexOutOfBoundsException ("No group " + sub);
    if (start[sub] == -1)
      return null;
    ...
  }

所以在 GNU Classpath 的例子中,null当替换字符串中指定的捕获组无法捕获任何内容时,将附加到字符串中。

3) Android开源项目-Java核心库

在安卓中,Matcher.appendReplacement调用私有方法appendEvaluated,这又直接附加结果group(int)到替换字符串。

public Matcher appendReplacement(StringBuffer buffer, String replacement) {
    buffer.append(input.substring(appendPos, start()));
    appendEvaluated(buffer, replacement);
    appendPos = end();
    return this;
}

private void appendEvaluated(StringBuffer buffer, String s) {
    boolean escape = false;
    boolean dollar = false;
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c == '\\' && !escape) {
            escape = true;
        } else if (c == '$' && !escape) {
            dollar = true;
        } else if (c >= '0' && c <= '9' && dollar) {
            buffer.append(group(c - '0'));
            dollar = false;
        } else {
            buffer.append(c);
            dollar = false;
            escape = false;
        }
    }
    // This seemingly stupid piece of code reproduces a JDK bug.
    if (escape) {
        throw new ArrayIndexOutOfBoundsException(s.length());
    }
}

Since Matcher.group(int)回报null对于捕获失败的捕获组,Matcher.appendReplacement追加null当替换字符串中引用捕获组时。

向您抱怨的两个人很可能是在 Android 上运行他们的代码。

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

java 正则表达式中捕获组的行为混乱 的相关文章

随机推荐

  • 在数组中找到一个对象?

    Swift 有类似的东西吗 findWhere在Underscore js 中 我有一个类型的结构数组T并想检查数组是否包含一个结构对象 其name财产等于Foo 尝试使用find and filter 但它们只适用于原始类型 例如Stri
  • 使用常规迭代器向后迭代,还是与反向迭代器作斗争?

    我最近了解了在 C 中使用反向迭代器的正确方法 特别是当您需要擦除迭代器时 看这个问题 and this one 这就是你应该这样做的方式 typedef std vector
  • 方案中定义表达式的类型

    简单地说 我的问题是Scheme中定义表达式的类型是什么 举个例子 define x 5 or define x lambda n n n 这对我来说有点混乱 有人可以帮忙吗 球拍内define是一种特殊形式而不是表达式 因此它本身没有值
  • 屏幕上的客户端矩形坐标

    如何获取窗口客户区相对于屏幕的坐标 我想过使用GetClientRect and ClientToScreen 另外 在浏览器窗口中是什么ClientRect 仅有矩形HTML其中显示的文档 或者它包括浏览器栏和弹出菜单 这可能会缩小尺寸H
  • Java - 从缓冲读取器(从套接字)读取正在暂停线程

    我有一个线程从缓冲读取器读取字符 从套接字创建 如下所示 inputStream new BufferedReader new InputStreamReader clientSock getInputStream 这段代码只能运行一次 例
  • Clojure 1.9 Windows 安装

    过去 我使用 clojure org 提供的 Windows 安装程序在我的各种 Windows 计算机上安装 clojure Clojure 1 9 已经发布几周了 但据我所知 仍然没有 Windows 安装程序或基于 Java 的通用安
  • 特定扩展名的 Directory.GetFiles

    有没有办法简化这个 linq 表达式 或者有更好的方法吗 Directory GetFiles dir SearchOption AllDirectories Where s gt s EndsWith jpg StringComparis
  • 尝试将图表、范围等插入到 Word 时,Excel 2010 VBA 中的复制/粘贴错误

    在研究这个错误时 我得出的结论是 它与剪贴板没有像应有的那样清除有关 这在我们使用 2003 时不是问题 但现在我们使用 2010 我也继承了这段代码来自不再在这里工作的人 运行时错误 4605 此方法或属性不可用 因为剪贴板为空或无效 这
  • SockJS Python 客户端

    我有一个依赖 Websockets 的网站 Java Spring 践踏 Websocket对于 Spring RabbitMQ SockJS 的某些功能 我们正在创建一个基于 Python 的命令行界面 我们希望添加一些使用 websoc
  • 从 WPF 中的 ViewModel 类(MVVM 模式)更新 UI

    我在我的第一个 WPF 应用程序中使用 MVVM 模式 并且我认为有一些非常基本的问题 当用户点击我的视图上的 保存 按钮时 将执行一个命令 该命令调用我的 ViewModel 中的 private void Save 问题是 Save 中
  • 在 Google Colab 上使用最新的 Python 版本

    Google Colab 安装了 Python 3 6 但它不是当前版本的 Python 我该如何在 Google Colab 上将 Python 升级到最新版本 常见问题解答说 Colaboratory 支持 Python 2 7 和 P
  • 在 Linux 上构建和使用用于 C++ 的纯 llvm 工具链

    假设这是可能的 有人可以告诉我 我如何配置 cmake 构建以在 ubuntu 16 04 上创建一个 纯 llvm 工具链 其中包括 clang lld libc libc abi libunwind llvm 编译器 rt 任何其他可能
  • 避免窗口获得焦点

    我正在使用虚拟键盘 问题是当我按下虚拟键盘上的按键时 需要发送数据的窗口会失去焦点 我怎样才能避免这种情况 当键盘窗体接收焦点时 它接收的部分消息是失去焦点的窗口的句柄 wParam 执行您需要执行的操作 并将焦点设置回失去焦点的窗口 编辑
  • 实数 - 如何确定是否需要 float 或 double?

    给定一个真实值 我们可以检查是否float数据类型足以存储数字 或者double是必须的 我知道精度因架构而异 是否有任何 C C 函数可以确定正确的数据类型 有关背景 请参阅每个计算机科学家都应该了解的浮点运算知识 不幸的是 我认为没有任
  • style,格式化切片运算符

    PEP 8没有提到切片运算符 据我了解 与其他运算符不同 它不应该被空格包围 spam 3 5 OK spam 3 5 NOT OK 当使用复杂的表达式时 这是否成立 即哪种风格被认为更好 1 spam ham 66 3 44 eggs 2
  • 如何实例化不同版本的 InternetExplorerDriver - Selenium 2?

    只是想知道如何实例化不同版本的 InternetExplorerDriver 这就是我创建 IE 驱动程序的方法 WebDriver ieWebDriver new InternetExplorerDriver 但我无法区分 IE6 IE7
  • Java中如何计算整数的尾随零? (例如:234000 => 3 个零)

    标题几乎是不言自明的 1232 gt 0 1231030 gt 1 2000 gt 3 34444400000 gt 5 如果它适合一个int long 只需检查模 10 的数字是否为 0 并保留一个计数器 long x if x 0 re
  • 如果我将变量“close”全局定义为“0”,为什么它会被记录为“false”?

    我知道这一定是非常基本的东西 但我不明白范围是如何工作的 我想要closed变量在整个 JavaScript 文件中都是已知的 我有类似的东西 在 jQuery 中 var closed 0 function console log clo
  • Youtube Iframe 没有全屏按钮

    也许是星期五下午 但由于某种原因 我似乎无法在嵌入的 Youtube 视频上显示全屏按钮 我所做的只是复制从 Youtube 视频生成的共享代码 例如使用这段代码http jsfiddle net chricholson v8sjL I s
  • java 正则表达式中捕获组的行为混乱

    In this answer我推荐使用 s replaceFirst 0 d 0 1 但有两个人抱怨结果包含字符串 null 例如 23 null 这可以解释为 1 i e group 1 being null 可以通过以下方式进行转换St