移除字符串的最后重复字符。

5

假设我们有这样的字符串:

"abcdaaaaefghaaaaaaaaa"
"012003400000000"

我希望删除最后重复的字符,以得到以下结果:
"abcdaaaaefgh"
"0120034"

有没有一种简单的方法可以使用正则表达式来实现这个功能?

我现在遇到了困难,我的代码开始变得非常庞大...

需要澄清的几点:

  • 什么被认为是重复的?

    结尾至少有 2 个字符连续出现。一个字符不被视为重复。例如:在 "aaaa" 中,'a' 不被视为重复,但在 "baaaa" 中,它是。所以在 "aaaa" 的情况下,我们无需对字符串进行任何更改。另一个例子: "baa" 必须返回 "b"

  • 那么对于只有一个字符的字符串呢?

    "a" 这样仅包含字符 'a' 的字符串必须原样返回,即我们必须返回 "a"


只有一个字符重复了吗? - hamid
最后一个字符是否总是重复的? - Loamhoof
不,最后一个字符并不总是重复的。它可以在末尾有至少2个字符的序列。一个字符不被视为重复。 - user1721413
5个回答

10
你可以使用 replaceAll() 方法与后向引用一起使用:
str = str.replaceAll("(.)\\1+$", "");

编辑

为了满足不能完全删除字符串的要求,我会在之后添加一个检查,而不是让正则表达式变得过于复杂:

public String replaceLastRepeated(String str) {
    String replaced = str.replaceAll("(.)\\1+$", "");
    if (replaced.equals("")) {
        return str;
    }
    return replaced;
}

2
该死!我正在写同样的东西 :) - Eugene
2
@Eugene 不仅是你,无论如何加一分给最快的枪手。 - Pshemo
如果我们使用这个正则表达式来匹配aaaa,它将返回空值。实际上应该返回aaaa - user1721413
@Pigrou:在这种情况下,您需要完善您的规格说明。什么情况下不应该删除最后几个字符? - Keppil

3

我不认为我会使用正则表达式来做这件事:

public static String removeRepeatedLastCharacter(String text) {
    if (text.length() == 0) {
        return text;
    }
    char lastCharacter = text.charAt(text.length() - 1);
    // Look backwards through the string until you find anything which isn't
    // the final character
    for (int i = text.length() - 2; i >= 0; i--) {
        if (text.charAt(i) != lastCharacter) {
            // Add one to *include* index i
            return text.substring(0, i + 1);
        }
    }
    // Looks like we had a string such as "1111111111111".
    return "";
}

个人认为这比正则表达式更容易理解。它可能更快,也可能不是 - 我不想做出预测。

请注意,这将始终删除最后一个字符,无论它是否重复。这意味着单个字符的字符串将始终变为空字符串:

"" => ""
"x" => ""
"xx" => ""
"ax" => "a"
"abcd" => "abc"
"abcdddd" => "abc"

1
不确定您是否正确处理了单字符情况和双字符情况。请使用“c”和“0120034000000001”进行测试以确认。 - OldCurmudgeon
@OldCurmudgeon:一个单独的字符最终会返回一个空字符串。两个字符应该没问题——它将进入for循环一次(i == 0),如果这两个字符不同,它将返回text.substring(0,1) - Jon Skeet
抱歉 - 我编辑了我的评论。我们似乎在解释上有分歧。没问题。 - OldCurmudgeon
@OldCurmudgeon:是的,两者的行为都符合我的预期。这绝对是由于OP没有准确表述问题。我会在我的回答中解释这种行为。 - Jon Skeet
我喜欢它不使用正则表达式的事实,但是如果我们例如使用这个正则表达式 aaaa,它会返回空值。它应该返回 aaaa - user1721413
我编辑了我的问题以使其更清晰,如果您有想法,可以调整您的答案。 - user1721413

3

我不会使用正则表达式:

public class Test {
  public void test() {
    System.out.println(removeTrailingDupes("abcdaaaaefghaaaaaaaaa"));
    System.out.println(removeTrailingDupes("012003400000000"));
    System.out.println(removeTrailingDupes("0120034000000001"));
    System.out.println(removeTrailingDupes("cc"));
    System.out.println(removeTrailingDupes("c"));
  }

  private String removeTrailingDupes(String s) {
    // Is there a dupe?
    int l = s.length();
    if (l > 1 && s.charAt(l - 1) == s.charAt(l - 2)) {
      // Where to cut.
      int cut = l - 2;
      // What to cut.
      char c = s.charAt(cut);
      while (cut > 0 && s.charAt(cut - 1) == c) {
        // Cut that one too.
        cut -= 1;
      }
      // Cut off the repeats.
      return s.substring(0, cut);
    }
    // Return it untouched.
    return s;
  }

  public static void main(String args[]) {
    new Test().test();
  }
}

为了匹配@JonSkeet的“规范”:
请注意,这将仅删除末尾重复的字符。这意味着单个字符的字符串不会被修改,但如果两个字符相同,则两个字符的字符串可能变为空:
"" => ""
"x" => "x"
"xx" => ""
"aaaa" => ""
"ax" => "ax"
"abcd" => "abcd"
"abcdddd" => "abc"

我想知道在正则表达式中是否可能实现那种级别的控制?

补充说明:基于 但是,如果我们使用此正则表达式例如对于aaaa,它不返回任何内容。应该返回aaaa。 的评论:

可以改为使用如下正则表达式:

  private String removeTrailingDupes(String s) {
    // Is there a dupe?
    int l = s.length();
    if (l > 1 && s.charAt(l - 1) == s.charAt(l - 2)) {
      // Where to cut.
      int cut = l - 2;
      // What to cut.
      char c = s.charAt(cut);
      while (cut > 0 && s.charAt(cut - 1) == c) {
        // Cut that one too.
        cut -= 1;
      }
      // Cut off the repeats.
      return cut > 0 ? s.substring(0, cut): s;
    }
    // Return it untouched.
    return s;
  }

合同归属方:

"" => ""
"x" => "x"
"xx" => "xx"
"aaaa" => "aaaa"
"ax" => "ax"
"abcd" => "abcd"
"abcdddd" => "abc"

我接受你的答案!你的编辑完全符合我的要求,而且看起来不像我那可怕的代码怪物,谢谢! - user1721413

0

使用正则表达式将(.)\1+$替换为空字符串:

"abcddddd".replaceFirst("(.)\\1+$", ""); // returns abc

0
这应该能解决问题:
public class Remover {
     public static String removeTrailing(String toProcess)
     {
        char lastOne = toProcess.charAt(toProcess.length() - 1);
        return toProcess.replaceAll(lastOne + "+$", "");
     } 

     public static void main(String[] args)
     {
        String test1 = "abcdaaaaefghaaaaaaaaa";
        String test2 = "012003400000000";

        System.out.println("Test1 without trail : " + removeTrailing(test1));
        System.out.println("Test2 without trail : " + removeTrailing(test2));
     }
}

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