如何使用正则表达式在字符串中去除重复字符?

3
我需要替换字符串中的重复字符。我尝试使用
outputString = str.replaceAll("(.)(?=.*\\1)", "");

这将替换重复的字符,但字符的位置会如下所示发生变化。

输入

haih

输出

aih

但我需要得到一个输出hai。也就是说,字符串中出现的字符顺序不应该改变。以下是一些输入的预期输出。

输入

aaaassssddddd

输出

asd

输入

cdddddggggeeccc

输出
cdge

这怎么能实现?

1
如果输入是 hhaiha,你的输出是什么? - Avinash Raj
@AvinashRaj hhaiha 将会得到 iha - Johny
1
所以你想要删除首次出现的重复项。那么你的正则表达式似乎是正确的。 - Avinash Raj
字符串中的字符应仅出现一次,字符出现的顺序不应更改。 - Johny
我还是不明白你的意思。一个例子不足以说明问题,所以请提供更多示例及其预期输出。 - Avinash Raj
@AvinashRaj 我添加了一些示例。 - Johny
2个回答

5

看起来你的代码漏掉了最后一个字符,那么尝试这个:

outputString = new StringBuilder(str).reverse().toString();
// outputString is now hiah
outputString = outputString.replaceAll("(.)(?=.*\\1)", "");
// outputString is now iah
outputString = new StringBuilder(outputString).reverse().toString();
// outputString is now hai

不? // ccceeeggggddddc // egdc // cdge - J. Titus
@AvinashRaj 对于 cdddddggggeeccc 的输出为 cdge 是正确的。 - Johny
@J.Titus 有没有其他直接使用正则表达式来完成这个任务的方法? - Johny

2

概述

使用Oracle的实现是可能的,但出于许多原因,我不建议采用这种方法:

  • 它依赖于实现中的错误,它将*+{n,}解释为{0, 0x7FFFFFFF}{1, 0x7FFFFFFF}{n, 0x7FFFFFFF},这允许后向查找包含这些量化器。由于它依赖于一个错误,所以不能保证它将来会以类似的方式工作。

  • 它是一团乱麻,难以维护。编写正常的代码和任何有基本Java知识的人都可以阅读它,但在这个答案中使用正则表达式限制了能够一眼理解代码的人数,只有那些了解正则表达式实现的人才能理解。

因此,这个答案是为教育目的而存在的,而不是用于生产代码。

解决方案

这里是一个一行的replaceAll正则表达式解决方案:

String output = input.replaceAll("(.)(?=(.*))(?<=(?=\\1.*?\\1\\2$).+)","")

打印出正则表达式:

(.)(?=(.*))(?<=(?=\1.*?\1\2$).+)

我们想要做的是向后查看,看同样的字符是否出现过。在开头的捕获组(.)捕获当前字符,并且向后查找组在那里检查字符是否之前出现过。到目前为止还不错。
然而,由于回溯引用\1没有明显的长度,它不能直接出现在向后查找中。
这就是我们利用错误向后查找字符串开始部分的地方,然后在向后查找内部使用向前查找来包括回溯引用的地方,正如您所看到的(?<=(?=...).+)
然而,问题并没有结束。虽然向后查找内部的非断言模式.+不能越过 (.)位置之后的位置,但是向前查找内部却可以。作为一个简单的测试:
"haaaaaaaaa".replaceAll("h(?<=(?=(.*)).*)","$1")
> "aaaaaaaaaaaaaaaaaa"

为确保搜索不超出当前字符,我使用先行断言 (?=(.*)) 捕获字符串的其余部分,并将其用于“标记”当前位置 (?=\\1.*?\\1\\2$)。
能否在不使用后行断言的情况下完成一次替换?
我认为这是不可能的。我们需要区分一个字符的第一次出现和同一字符的后续出现。虽然我们可以针对一个固定字符(例如 a)进行此操作,但问题要求我们对字符串中的所有字符都进行此操作。
供您参考,这是用于删除所有后续出现的固定字符(此处使用 h):
.replaceAll("^([^h]*h[^h]*)|(?!^)\\Gh+([^h]*)","$1$2")

为了对多个字符执行此操作,我们必须跟踪该字符是否出现过,跨匹配所有字符。上面的正则表达式显示了跨匹配部分,但另一个条件使这种情况变得不可能。
显然,我们不能在单个匹配中完成此操作,因为后续出现的次数可能是不连续和任意的。

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