Java 模式涉及美元符号($)时的正则表达式

Java 模式涉及美元符号($)时的正则表达式,java,regex,Java,Regex,当涉及到匹配涉及美元符号的子模式时,我遇到了一点问题。例如,考虑下面的一大块文本: (en $) foo oof ($). ofo (env. 80 $US) 我正在使用以下正则表达式: Pattern p = Pattern.compile( "\\([\\p{InARABIC}\\s]+\\)|\\([\\p{InBasic_Latin}\\s?\\$]+\\)|\\)([\\p{InARABIC}\\s]+)\\(", Pattern.CA

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

(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;
    }

但无法匹配包含
$

的文本此代码类似于
replaceAll(regex,replacement)
。问题是,
$
不仅在regex参数中是特殊的,而且在替换中,它可以用作组匹配的引用,如
$x
(其中
x
是组ID)或
${groupName}
,如果您的regex具有
(?subsectex)

这允许我们编写类似于

String doubled = "abc".replaceAll(".", "$0$0");
System.out.println(doubled); //prints: aabbcc
它将用其两个副本替换每个字符,因为每个字符都将由
匹配并放入组0中,因此
$0$0
表示该匹配字符的两个重复

但是在您的案例中,您的
文本中有
$
,因此当它匹配时,您将用它本身替换它,因此您在替换
$
中使用,而没有任何关于组ID(或组名)的信息,这将导致
非法组引用

解决方案是在替换零件中转义
$
。您可以使用
\
手动执行此操作,但最好使用为此目的设计的方法(如果正则表达式会演变,并且您需要逃避更多事情,则此方法应与正则表达式引擎一起演变,这将为您以后节省一些麻烦)

因此,请尝试将代码更改为

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;
    }
}

此代码类似于
replaceAll(regex,replacement)
。问题是,
$
不仅在regex参数中是特殊的,而且在替换中,它可以用作组匹配的引用,如
$x
(其中
x
是组ID)或
${groupName}
,如果您的regex具有
(?subsectex)

这允许我们编写类似于

String doubled = "abc".replaceAll(".", "$0$0");
System.out.println(doubled); //prints: aabbcc
它将用其两个副本替换每个字符,因为每个字符都将由
匹配并放入组0中,因此
$0$0
表示该匹配字符的两个重复

但是在您的案例中,您的
文本中有
$
,因此当它匹配时,您将用它本身替换它,因此您在替换
$
中使用,而没有任何关于组ID(或组名)的信息,这将导致
非法组引用

解决方案是在替换零件中转义
$
。您可以使用
\
手动执行此操作,但最好使用为此目的设计的方法(如果正则表达式会演变,并且您需要逃避更多事情,则此方法应与正则表达式引擎一起演变,这将为您以后节省一些麻烦)

因此,请尝试将代码更改为

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;
    }
}

您使用的是
find()
还是
matches()
?对不起,忘了提及我使用的是matcher您使用的是
匹配项
还是
查找
<代码>匹配项
不起作用,因为字符串与正则表达式不匹配。此外,如果您正确地使用了
find
,即使这样,您的第三个示例也不会提供任何信息,因为它包含一个
,而在正则表达式中没有“通常的元字符是字符类中的普通字符,不需要用反斜杠转义”。“如果转义字符类中的常规元字符,则正则表达式可以正常工作,但这样做会显著降低可读性。”您使用的是
find()
还是
matches()
?抱歉忘了提及我使用的是Matcher您使用的是
matches
还是
find
matches
将不起作用,因为您的字符串与正则表达式不匹配。此外,如果您正确使用了
find
,即使是这样,您的第三个示例也不会给您任何东西,因为其中包含一个不存在的
在正则表达式中,“通常的元字符是字符类中的普通字符,不需要反斜杠转义”。“如果转义字符类中的常规元字符,正则表达式可以正常工作,但这样做会显著降低可读性。”谢谢,我保留此模式:
pattern p=pattern.compile(“\\([\\p{InARABIC}\\s]+\\)\\([\\p{InBasic\u Latin}\\s?\\$]+\\)([\\p{InARABIC}\\s]+)\(“,Pattern.Pattern不区分大小写)
应该是这样的,不必客气。顺便说一句,我不知道你为什么要使用
模式。不区分大小写的
标志。通常当正则表达式包含显式字符,如
a
b
时使用它,你希望它们也表示
a
b
,但字符类如
\w
,我假设
\p{InBasic\u Latin}
默认情况下应包含任何字符的所有形式(大写和小写)。这不适用于
\p{InARABIC}
,还是您添加该标志只是为了安全?我保留该标志只是为了安全,正如您所提到的。谢谢,我保留此模式:
模式p=pattern.compile([\\p{InARABIC}\\s]+\\)|\\([\\p{InBasic\u Latin}\\s?\\$]+\\)([\\p{InARABIC}\\s]+)\\(“,Pattern.Pattern不区分大小写)
应该是这样的,不必客气。顺便说一句,我不知道你为什么要使用
模式。不区分大小写的
标志。通常当正则表达式包含显式字符,如
a
b
时使用它,你希望它们也表示
a
b
,但字符类如
\w
,我假设默认情况下,
\p{InBasic\u Latin}
应包含任何字符的所有形式(大写和小写)。这不适用于
\p{InARABIC}
,或者您添加该标志只是为了安全?我保留该标志只是为了安全,正如您所述。