在Java中用多个正则表达式替换字符

3

我有以下字符串:

String str = "Klaße, STRAßE, FUß";

使用组合正则表达式,我想将德语中的ß字母替换为相应的ssSS。 为此,我有:

String replaceUml = str
        .replaceAll("ß", "ss")
        .replaceAll("A-Z|ss$", "SS")
        .replaceAll("^(?=^A-Z)(?=.*A-Z$)(?=.*ss).*$", "SS");

期望结果:

Klasse, STRASSE, FUSS

实际结果:

Klasse, STRAssE, FUSS

我错在哪里了?


1
我不确定我理解你认为你的表达式是做什么的。第一个将 ß 替换为 ss,第二个接受单词末尾的 ss(或字符串 A-Z)并将其替换为 SS(这就是为什么 FUSS 是正确的),但我无法弄清楚你认为第三个应该做什么... 你能澄清一下吗? - Floris
@Floris 第三个函数的作用是查找以大写字母开头和结尾,并且中间包含“ss”的字符串。如果所有条件都成立,则将“ss”替换为“SS”。 - bofanda
如果我正确理解你的意图,那么你正在尝试用小写的 ss 替换字符串,如果该字符串仅包含小写字符,则使用大写的 SS 进行替换。第三个替换我仍然无法弄清楚你想要实现什么,你可能将字符类范围与字面字符匹配混淆了。 - Ibrahim Najjar
4个回答

4

首先,如果你想匹配 A-Z 范围内的某个字符,你需要将它放在方括号中。这个

.replaceAll("A-Z|ss$", "SS")

将在源代码中查找A-Z三个字符,这并不是您想要的。其次,我认为您对符号|的含义感到困惑。如果您这样说:

.replaceAll("[A-Z]|ss$", "SS")

它将用SS替换单词末尾的任何大写字母,因为|表示查找这个或那个。

你的方法存在第三个问题,即第二和第三个replaceAll将查找原始字符串中的任何ss,即使它不是来自ß。这可能是你想要的,也可能不是。

这是我会做的:

String replaceUml = str
    .replaceAll("(?<=[A-Z])ß", "SS")
    .replaceAll("ß", "ss");

如果ß前面的字符是大写字母,则首先将所有的ß替换为SS;然后,如果还有任何ß剩余,则将其替换为ss。实际上,如果ß之前的字符是像Ä这样的变音符号,则此方法不起作用,因此您可能需要更改此方法:

String replaceUml = str
    .replaceAll("(?<=[A-ZÄÖÜ])ß", "SS")
    .replaceAll("ß", "ss");

(可能有更好的方式来指定“大写Unicode字母”; 我会寻找它。)
编辑:
String replaceUml = str
    .replaceAll("(?<=\\p{Lu})ß", "SS")
    .replaceAll("ß", "ss");

一个问题是,如果ß是文本中的第二个字符,并且单词的第一个字母大写,但单词的其余部分不是大写的,则它将无法工作。在这种情况下,您可能希望使用小写的“ss”。
String replaceUml = str
    .replaceAll("(?<=\\b\\p{Lu})ß(?=\\P{Lu})", "ss")
    .replaceAll("(?<=\\p{Lu})ß", "SS")
    .replaceAll("ß", "ss");

现在,第一条规则将在 ß 前面是大写字母并且后面不是大写字母的情况下将其替换为 ss。 使用大写字母 P 的 \P{Lu} 将匹配除大写字母外的任何字符(它是小写字母 p 的反义词 \p{Lu})。 我们还包括了 \b 来测试单词的第一个字符。


+1 但我认为你还需要向前看,以防 Aßbc 的情况,你想要 Assbc(不知道德语单词是否存在第二个字母 ß,但从模式方面来看,这是一个边缘情况)。 - Bohemian
是的,我和你想的一样。而且我怀疑像那样以 "t" 或 "p" 结尾的单词确实存在,但我不确定,而且为了让事情更有趣,德国政府最近改变了使用 ß 的规则,所以我在高中学习的德语可能已经过时了。 - ajb

2
String replaceUml = str
    .replaceAll("(?<=\\p{Lu})ß", "SS")
    .replace("ß", "ss")

这里使用了 正则表达式,并且在一个 Unicode 大写字母 ("SÜß") 前面加上一个大写的 "SS"。

(?<= ... ) 是一个后顾断言,一种上下文匹配方式。你也可以这样写:

    .replaceAll("(\\p{Lu})ß", "$1SS")

由于 ß 不会出现在单词开头,因此您需要注意这一点。

您的主要问题是没有使用方括号 [A-Z]


0
将你的正则表达式拆分为部分:

正则表达式 101 演示

正则表达式

/ß/g

描述

ß Literal ß
g modifier: global. All matches (don't return on first match)

可视化

Regular expression visualization


正则表达式 101 演示

正则表达式

/([A-Z])ss$/g

描述

1st Capturing group ([A-Z]) 
    Char class [A-Z]  matches:
        A-Z A character range between Literal A and Literal Z
ss Literal ss
$ End of string
g modifier: global. All matches (don't return on first match)

可视化

Regular expression visualization


正则表达式 101 演示

正则表达式

/([A-Z]+)ss([A-Z]+)/g

描述

1st Capturing group ([A-Z]+) 
    Char class [A-Z] 1 to infinite times [greedy] matches:
        A-Z A character range between Literal A and Literal Z
ss Literal ss
2nd Capturing group ([A-Z]+) 
    Char class [A-Z] 1 to infinite times [greedy] matches:
        A-Z A character range between Literal A and Literal Z
g modifier: global. All matches (don't return on first match)

可视化

Regular expression visualization


专为您定制

String replaceUml = str
    .replaceAll("ß", "ss")
    .replaceAll("([A-Z])ss$", "$1SS")
    .replaceAll("([A-Z]+)ss([A-Z]+)", "$1SS$2");

1
嗯,那不会删除ss前面的字符吗? - ajb
是的,如果您点击链接,我使用了捕获组,只是在解决方案编辑代码中没有使用。 - abc123
你需要使用双反斜杠 \1 和 \2。 - ajb
1
这是Java,不是JavaScript。 - Bohemian
是的,我之前没有注意到。 (另外,这不是Perl。) 应该是$1$2\\1\\2不起作用。 - ajb

-1
使用 String.replaceFirst() 代替 String.replaceAll()。
replaceAll("ß", "ss")

这将替换所有 "ß" 的出现。因此,在执行此语句之后,输出变为:

Klasse, STRAssE, FUss

现在,replaceAll("A-Z|ss$", "SS") 将 "ss" 的最后一个出现替换为 "SS",因此您的最终结果如下:

Klasse, STRAssE, FUSS

要获得您的 期望结果,请尝试以下内容:

String replaceUml = str.replaceFirst("ß", "ss").replaceAll("ß", "SS");

根据您的建议,我现在得到了以下结果:Klasse,STRAssE,FUss - bofanda
试试使用 replaceFirst()。它会有所帮助的。 :-) - Ankur Shanbhag
根据您的回答,我们得到了结果:“Klasse,STRAssE,FUSS”,但我想要“Klasse,STRASSE,FUSS”。 - bofanda
请查看我的回答的最后一部分,我已经粘贴了代码以获取您期望的结果。希望这能有所帮助。 - Ankur Shanbhag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接