【问题标题】:Regex when pattern involves dollar sign ($)模式涉及美元符号 ($) 时的正则表达式
【发布时间】:2026-02-05 04:05:01
【问题描述】:

在匹配涉及美元符号的子模式时,我遇到了一些问题。例如,考虑以下文本块:

(en $) foo
oof ($).
ofo (env. 80 $US)

我正在使用以下正则表达式:

Pattern p = Pattern.compile(
            "\\([\\p{InARABIC}\\s]+\\)|\\([\\p{InBasic_Latin}\\s?\\$]+\\)|\\)([\\p{InARABIC}\\s]+)\\(",
            Pattern.CASE_INSENSITIVE);

public String replace(String text) {
    Matcher m = p.matcher(text);
        String replacement = m.replaceAll(match -> {
            if (m.group(1) == null) {
                return m.group();
            } else {
                return "(" + match.group(1) + ")";
            }
        });
        return replacement;
    }

但无法匹配包含$的文本

【问题讨论】:

  • 你使用的是find()还是matches()
  • 抱歉忘了说我正在使用匹配器
  • 您使用的是matches 还是findmatches 不起作用,因为您的字符串与正则表达式不匹配。此外,如果您正确使用find,即使您的第三个样本也不会给您任何东西,因为它里面包含一个.,而您的正则表达式中没有这个
  • regular-expressions.info/charclass.html "通常的元字符是字符类中的普通字符,不需要用反斜杠转义".
  • “如果您转义字符类中的常规元字符,您的正则表达式可以正常工作,但这样做会大大降低可读性。”

标签: java regex


【解决方案1】:

此代码类似于replaceAll(regex, replacement)。问题是 $ 不仅在 regex 参数中是特殊的,而且在 replacement 中也很特殊,它可以用作来自 $x 等组的匹配参考(其中x 是组 ID)或 ${groupName} 如果您的正则表达式有 (?<groupName>subregex)

这允许我们编写类似的代码

String doubled = "abc".replaceAll(".", "$0$0");
System.out.println(doubled); //prints: aabbcc

它将用它的两个副本替换每个字符,因为每个字符将与 . 匹配并放置在第 0 组中,因此 $0$0 表示该匹配字符的两次重复。

但在您的情况下,您的 text 中有 $,因此当它匹配时,您将用它自己替换它,因此您使用的是替换 $ 没有任何关于组 ID 的信息 em>(或组名)导致IllegalArgumentException: Illegal group reference

解决方案是替换部分中转义$。您可以使用\ 手动执行此操作,但最好使用为此目的设计的方法Matcher#quoteReplacement(如果正则表达式会发展并且您需要转义更多内容,则此方法应与正则表达式引擎一起发展,这应该以后省点麻烦)

所以尝试将您的代码更改为

public String replace(String text) {
    Matcher m = p.matcher(text);
        String replacement = m.replaceAll(match -> {
            if (m.group(1) == null) {
                return Matcher.quoteReplacement(m.group());
                //     ^^^^^^^^^^^^^^^^^^^^^^^^
            } else {
                return Matcher.quoteReplacement("(" + match.group(1) + ")");
                //     ^^^^^^^^^^^^^^^^^^^^^^^^
            }
        });
        return replacement;
    }
}

【讨论】:

  • 谢谢,我保留了这个模式:Pattern p = Pattern.compile( "\\([\\p{InARABIC}\\s]+\\)|\\([\\p{InBasic_Latin}\\s?\\$]+\\)|\\)([\\p{InARABIC}\\s]+)\\(", Pattern.CASE_INSENSITIVE);应该是这样的
  • 不客气。顺便说一句,我不确定您为什么使用 Pattern.CASE_INSENSITIVE 标志。通常,当正则表达式包含显式字符(如 a b 并且您希望它们也表示 AB,但像 \w 和我假设 \p{InBasic_Latin} 这样的字符类默认情况下应该包含所有任何字符的形式(大写和小写)。这不适用于\p{InARABIC},还是为了安全起见您添加了该标志?
  • 我保留了那个标志只是为了安全起见。