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

时间:2014-11-08 03:31:54

标签: java regex

我需要替换字符串中的重复字符。我尝试使用

outputString = str.replaceAll("(.)(?=.*\\1)", "");

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

输入

haih

输出

aih

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

输入

aaaassssddddd

输出

asd

输入

cdddddggggeeccc

输出

cdge

如何实现这一目标?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:2)

概述

Oracle的实施有可能,但出于多种原因我不推荐这个答案:

  • 它依赖于bug in the implementation,它将*+{n,}解释为{0, 0x7FFFFFFF}{1, 0x7FFFFFFF},{{分别为1}},它允许后视包含这样的量词。由于它依赖于一个bug,因此不能保证它将来会有类似的工作。

  • 这是不可维护的混乱。编写普通代码和任何具有一些基本Java知识的人都可以阅读它,但是在这个答案中使用正则表达式限制了那些能够理解代码的人数,这些人了解正则表达式实现的进出。

因此,这个答案是出于教育目的,而不是在生产代码中使用的东西。

解决方案

以下是单行{n, 0x7FFFFFFF}正则表达式解决方案:

replaceAll

打印出正则表达式:

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

我们想要做的是后视,看看之前是否出现过相同的角色。开头的捕获组(.)(?=(.*))(?<=(?=\1.*?\1\2$).+) 捕获当前角色,后视组用于检查角色是否已经出现过。到目前为止,非常好。

但是,由于反向引用(.)没有明显的长度,因此它不会直接出现在后视中。

这是我们利用这个bug来查看字符串开头的地方,然后在后视图中使用前瞻来包含反向引用,正如您所见\1。 .. (?<=(?=

但这不是问题的结束。虽然后视).+)内的非断言模式无法超越.+中的角色之后的位置,但内部的前瞻可以。作为一个简单的测试:

(.)

为了确保搜索不会溢出当前字符,我会在前瞻"haaaaaaaaa".replaceAll("h(?<=(?=(.*)).*)","$1") > "aaaaaaaaaaaaaaaaaa" 中捕获字符串的其余部分并将其用于&#34;标记&#34;当前位置(?=(.*))

这可以在一次更换中完成吗,而不使用后视?

我认为这是不可能的。我们需要区分角色的第一个外观和随后出现的相同角色。虽然我们可以针对一个固定字符(例如(?=\\1.*?\\1\\2$))执行此操作,但问题要求我们对字符串中的所有字符执行此操作。

有关您的信息,这是为了删除固定字符的所有后续外观(此处使用a):

h

要对多个角色执行此操作,我们必须先跟踪角色是否出现过,跨越匹配表示所有角色。上面的正则表达式显示跨越匹配部分,但另一个条件有点使得这不可能。

我们显然无法在一次匹配中执行此操作,因为后续事件的数量可能是非连续且任意的。