Java字符串,从右侧开始每8个字符插入一个破折号

9

如何在Java字符串中从右侧开始每8个字符插入一个“-”(短横线/减号)是最好的方法?

示例:

1111 -> 1111
111111111 -> 1-11111111
1111111111111111 -> 11111111-11111111
100001111111111111111 -> 10000-11111111-11111111

我试图自己完成这个任务(下面的评论问:“这是作业吗?”):

import junit.framework.TestCase;
public class InsertCharacterAfterEveryNCharacters extends TestCase {
  public static String insertSpacerAfterNCharactersFromTheRight(char spacer,
      int spacing, String string) {
    final int length = string.length();
    final int newStringCapacity = length + (int) Math.ceil(length / (double) spacing);
    StringBuilder stringBuilder = new StringBuilder(newStringCapacity);
    for (int i = length - 1; i >= 0; i--) {
      stringBuilder.append(string.charAt(i));
      if (i % spacing == 0 && i > 0) {
        stringBuilder.append(spacer);
      }
    }
    return stringBuilder.toString();
  }
  public static void testInsertSpacerAfterNCharactersFromTheRight() {
    assertEquals("", insertSpacerAfterNCharactersFromTheRight('-', 8, ""));
    assertEquals("1", insertSpacerAfterNCharactersFromTheRight('-', 8, "1"));
    assertEquals("11", insertSpacerAfterNCharactersFromTheRight('-', 8, "11"));
    assertEquals("11111111",
        insertSpacerAfterNCharactersFromTheRight('-', 8, "11111111"));
    assertEquals("1-11111111",
        insertSpacerAfterNCharactersFromTheRight('-', 8, "111111111"));
    assertEquals("11111111-11111111",
        insertSpacerAfterNCharactersFromTheRight('-', 8, "1111111111111111"));
  }
}

5
没什么花哨的,但是一个好用的for循环就可以解决问题。使用新的StringBuffer(key).insert(position, "-").toString(); - thatidiotguy
对于那个循环,首次迭代处理(string.length%8)个字符,之后跳过8个字符。在循环中使用StringBuilder,这样就不必一直创建新的String对象,只需在每次迭代中附加字符串前缀即可。 - G. Bach
你说得对,让我添加我的解决方案(正确地)表明在发布之前我已经做了一些工作。 - Robottinosino
关于那个获得5个赞的评论,推荐使用“insert()”,这样做不会在每次调用时执行不必要的ArrayCopy吗? - Robottinosino
5个回答

34

所有的答案看起来都需要很多代码才能完成任务。你可以使用正则表达式来实现。

假设你有一个返回格式化字符串的方法。

一个简单、清晰的例子:

String myString = "00000000000111111111111100000000001111111000011000000";
String newString = myString.replaceAll("(.{8})(?!$)", "$1-");
return newString;

以上等于以下更简短的表述:

return myString.replaceAll("(.{8})(?!$)", "$1-");

在字符串是固定的、硬编码的情况下,还有另一种类似的简短表示方法:

return "00000000000111111111111100000000001111111000011000000".replaceAll("(.{8})(?!$)", "$1-");
每段代码都返回以下字符串:
00000000-00011111-11111111-00000000-00111111-10000110-00000

关于正则表达式的更多信息,请参见例如http://www.regular-expressions.info/java.html

希望这对未来的任何人都有所帮助。

编辑说明: 最后一个组没有8个字符。 但是,如果有,它将不会再添加另一个破折号。


从“右侧”插入“-”? - Robottinosino
你可以使用 StringBuilder 来反转字符串,将正则表达式应用于反转后的字符串,然后再次反转以获得所需的结果。 - ar34z
昂贵,但还是谢谢。 - Robottinosino

17

从原始的String构建一个char[]

String str = "100001111111111111111";

// add enough space for an additional "-" for every 8 chars:
char[] chars = new char[str.length() + (str.length() / 8)];

// this offset will give us the first "-" position from the LEFT:
int offset = str.length() % 8;
int idx = 0, strIdx = 0;

for (; strIdx < str.length(); idx++, strIdx++)
{
    if (((strIdx % 8) == offset) && (strIdx != 0))
        chars[idx++] = '-';
    chars[idx] = str.charAt(strIdx);
}

String str2 = new String(chars);

System.out.println(str2);

或者你可以使用 StringBuilder,这可能涉及到 (length / 8) 次数组复制操作:

StringBuilder str = new StringBuilder("100001111111111111111");
int idx = str.length() - 8;

while (idx > 0)
{
    str.insert(idx, "-");
    idx = idx - 8;
}

System.out.println(str.toString());

两种情况下的输出:

10000-11111111-11111111

请注意,这不会在索引0处插入连字符,如果需要,您需要调整代码。


这个答案已经得到了3个赞,但我想知道:每8个字符是否真的需要执行完整的System.arraycopy(value, offset, value, offset + len, count - offset);,而不是创建正确大小的缓冲区并逐步填充它? - Robottinosino
@Robottinosino 说得好,这个解决方案太简单了。我补充了数组操作的解决方案。 - pb2q
太棒了,解决方案非常好! :D - cokeby190

1
上述解决方案虽然不错,但非常复杂且冗长。我写了这个代码,它可以处理您的输入。
public static String insertCharacterForEveryNDistanceFromRight(int distance, String original, char c){
    StringBuilder sb = new StringBuilder();
    char[] charArrayOfOriginal = original.toCharArray();
    for(int ch = charArrayOfOriginal.length ; ch > 0 ; ch--){
        if(ch % distance == 0 && ch != charArrayOfOriginal.length)
            sb.append(c).append(charArrayOfOriginal[charArrayOfOriginal.length - ch]);
        else
            sb.append(charArrayOfOriginal[charArrayOfOriginal.length - ch]);
    }
    return sb.toString();
}

并且像这样调用它...
String rightResult = InsertSpaces.insertCharacterForEveryNDistanceFromRight(8, "100001111111111111111", '-');
System.out.println(rightResult);

0

可能是这样的吗?

for(int i = s.length() - 1; i > 0; i -= 8)
{
    s = new StringBuffer(s).insert(i, "-").toString();
    i--;
}

1
请查看pb2q的回答,你会学到很好的东西。 - Luiggi Mendoza
嗯,你能提供更多的信息吗?如果你是在说他的代码更好,那很好,但更多的信息会更好。是因为我正在创建多个StringBuffer吗? - thatidiotguy
只是好奇,如果你将pb2q的答案作为参考来批评另一个答案,为什么不给它点个赞呢? - ametren
@thatidiotguy 确实,我应该在我的评论中添加一些信息:首先:StringBuffer 支持同步,而 StringBuilder 不支持,因此根据字符串长度,这可能会稍微提高性能。其次:没有必要在每次迭代中创建 StringBuffer(或 StringBuilder),您可以在开头创建它,并在每个 for 循环迭代中插入值。 - Luiggi Mendoza
@ametren 当我评论这个帖子时,pb2q的答案只有使用StringBuilder的解决方案,我不认为这是最适合的,而是一个快速的解决方法。现在我午饭后回来,看到了使用char[]的解决方案,这更有趣和有效(而且我倾向于这样编码),现在他得到了我的赞同。 - Luiggi Mendoza

0

你不能直接在现有的Java字符串中插入-,因为String是不可变的。但是,你可以创建一个新的String实例来满足你的输出格式要求。

从输入字符串的末尾索引开始循环到0。对于每次迭代:

  • 将当前索引处的值插入到StringBuilder实例的开头。
  • 使用模运算符查看是否已经插入了8个字符的倍数。如果是,则插入破折号。

如果需要,使用StringBuilder实例的"toString()"方法获取字符串。


我突然想到,如果他事先指定StringBuilder的长度,这个程序会不会表现得更好呢?我不确定StringBuilder在内部如何处理字符字符串。 - G. Bach
是的,可能可以,但是我敢打赌你测不出来这两种方法的差异:-) 这取决于最终字符串的长度与 StringBuilder 分配的初始缓冲区的比例,以及进行总长度计算的成本。 - Eric J.
1
总长度计算是一个简单的算术运算:字符串长度 + 字符串长度/8 + 1。 - G. Bach
@G.Bach:是的,但对于小内存块来说,内存移动操作也不是非常昂贵的。我们正在针对较小的输入字符串进行微优化。 - Eric J.

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