9

What's the best way to insert a - (dash/minus character) after every 8 characters in a Java String, starting from the right?

Examples:

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

My attempt, to show that I have tried doing it myself (a comment below asks: "is this homework?":

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"));
  }
}
Robottinosino
  • 10,384
  • 17
  • 59
  • 97
  • 5
    Nothing fancy, but a good old for loop would do the trick. Use new StringBuffer(key).insert(position, "-").toString(); – thatidiotguy Aug 15 '12 at 17:40
  • For that loop, do the first iteration for (string.length%8) characters and after that skip 8 characters. Use StringBuilder in the loop so you don't have to create new String objects all the time and just append the prefix of the String in every iteration. – G. Bach Aug 15 '12 at 17:42
  • You are right, let me add my own solution to (rightly) show that I have done some work of my own before posting. – Robottinosino Aug 15 '12 at 17:59
  • About the comment that got 5 upvotes recommending "insert()", does this not perform an unnecessary ArrayCopy at each invocation? – Robottinosino Aug 15 '12 at 18:18

5 Answers5

34

All answers seem a bit lot of code for what needs to be done. You could use a regular expression to do that.

Let's say you have a method that returns your formatted string.

A simple, clear example:

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

Above is equal to the following shorter notation:

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

Another similar, short notation (in case of a fixed, hard-coded string):

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

Each piece of code resturns the following string:

00000000-00011111-11111111-00000000-00111111-10000110-00000

For more info on regular expressions, see for example http://www.regular-expressions.info/java.html

Hope this helps anyone in the future.

Edit note: The last group doesn;t have 8 characters. However, if it would, it won't add another dash.

ar34z
  • 2,609
  • 2
  • 24
  • 37
17

Build up a char[] from the original String:

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);

Or you could use a StringBuilder, which could involve (length / 8) array copy operations:

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

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

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

Output in either case:

10000-11111111-11111111

Note that this won't insert a hyphen at index 0, you'll need to adjust the code if that's necessary.

pb2q
  • 58,613
  • 19
  • 146
  • 147
  • This answer has got 3 upvotes but I wonder: is it really necessary to perform a full `System.arraycopy(value, offset, value, offset + len, count - offset);` every 8 characters rather than creating the buffer of the right size and populate it incrementally? – Robottinosino Aug 15 '12 at 18:13
  • @Robottinosino good point, the solution is naive. Added the array manipulation solution. – pb2q Aug 15 '12 at 18:25
1

The above solutions are good but are very complicated and long, I wrote this and it works for your inputs.

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();
}

And call it like this...

String rightResult = InsertSpaces.insertCharacterForEveryNDistanceFromRight(8, "100001111111111111111", '-');
System.out.println(rightResult);
Arif Nadeem
  • 8,524
  • 7
  • 47
  • 78
0

You cannot literally insert a - into an existing Java String because String are immutable. However, you can create a new String instance that meets your output format requirement.

Loop from the end index of your input string down to 0. For each iteration:

  • Insert the value at the current index to the beginning of a StringBuilder instance.
  • Use the modulo operator to see if you have inserted a multiple of 8 characters. If so, insert a dash.

If needed, use the "toString()" method of your StringBuilder instance to get a string.

Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • It just occurred to me, would this perform better if he specified the length of the StringBuilder beforehand? I'm not sure how StringBuilder internally handles the character string. – G. Bach Aug 15 '12 at 17:44
  • Yes, possibly, but I dare you to measure the difference :-) It depends on the length of the final string in relation to the initial buffer allocated by StringBuilder, vs. the cost of doing that total length calculation. – Eric J. Aug 15 '12 at 17:45
  • 1
    The total length calculation is a simple arithmetic operation: string.length + string.length/8 + 1 – G. Bach Aug 15 '12 at 17:53
  • @G.Bach: Yes, but the memory move operation is really not very expensive either (for small memory blocks). We're looking at a micro-optimization for smallish input strings. – Eric J. Aug 15 '12 at 18:38
0

Something like this perhaps?

for(int i = s.length() - 1; i > 0; i -= 8)
{
    s = new StringBuffer(s).insert(i, "-").toString();
    i--;
}
thatidiotguy
  • 8,701
  • 13
  • 60
  • 105
  • 1
    Look for the [pb2q's answer](http://stackoverflow.com/a/11974333/1065197). You will learn something good. – Luiggi Mendoza Aug 15 '12 at 17:47
  • Um, could you include more information than that? If you are saying that his code is better fine, but more information would be nice. Is it because I am creating multiple StringBuffers? – thatidiotguy Aug 15 '12 at 17:50
  • Just curious -- why didn't you +1 pb2q's answer if you're using it as a reference to critique another answer? – ametren Aug 15 '12 at 17:51
  • @thatidiotguy true, I've should added some info on my comment: first: `StringBuffer` supports synchronization while `StringBuilder` doesn't so this could be slightly better for performance depending on the String length. Second: there's no need to create the StringBuffer (or StringBuilder) on every iteration, you can create it at the beginning and insert the values on every for loop iteration. – Luiggi Mendoza Aug 15 '12 at 19:14
  • @ametren when I commented this post, pb2q's answer just had the StringBuilder solution, that I don't consider as the most proper but a first glance one. Now I came back after lunch and there is the solution with the `char[]`, this is more interesting and effective (and I'm tended to code like that), now he has my vote up. – Luiggi Mendoza Aug 15 '12 at 19:17