我需要替换字符串中的重复字符。我尝试使用
outputString = str.replaceAll("(.)(?=.*\\1)", "");
这将替换重复的字符,但字符的位置会发生变化,如下所示。
haih
aih
但我需要输出hai
。也就是说,字符串中出现的字符的顺序不应该改变。以下是某些投入的预期产出。
aaaassssddddd
asd
cdddddggggeeccc
cdge
如何实现这一目标?
答案 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
要对多个角色执行此操作,我们必须先跟踪角色是否出现过,跨越匹配,表示所有角色。上面的正则表达式显示跨越匹配部分,但另一个条件有点使得这不可能。
我们显然无法在一次匹配中执行此操作,因为后续事件的数量可能是非连续且任意的。