简体   繁体   中英

Remove every 8th char from a string

I have a string, which I want to iterate through and remove every 8th char. I have been trying with an modulo operation which check if i % 8 == 0 . However, since I remove every 8th char the length of the string decreases, and I am therefore unable to perform that operation.

StringBuilder str = "1100110001011011000000000000000000000000000000000000000000000000";
System.out.println(str + " " + str.length());

for (int i = 0; i < str.length(); i++) {

    // Every 8th element should be discarded    
    if (i > 7 && i % 8 == 0) {
        str.deleteCharAt(i);
    }
}

System.out.println(str + " " + str.length());

The length of the string is in the beginning 64, and after the for loop 57, which should be 56.

There is not deleteCharAt method in String , so I suppose you meant StringBuilder ?

You can just reverse the direction of the for loop, so that it starts from the end of the string:

    String str = "11111111811111118";
    StringBuilder builder = new StringBuilder(str);
    System.out.println(str + " " + str.length());

    for (int i = str.length() - 1; i >= 0; i--) {
        // Every 8th element should be discarded

        if (i > 7 && i % 8 == 0) {
            builder.deleteCharAt(i);
        }
    }

    System.out.println(builder+ " " + builder.length());

By deleting chars from the end of the string, the indices of the chars to be removed no longer changes as you move along the string.

Why don't you use regex and achieve it in two lines of code like this,

public static void main(String[] args) {
    String str = "1100110001011011000000000000000000000000000000000000000000000000";
    String replacedStr = str.replaceAll("([01]{7})[01]", "$1");

    System.out.println(str.toString() + " " + str.length());
    System.out.println(replacedStr.toString() + " " + replacedStr.length());
}

This gives perfectly correct output,

1100110001011011000000000000000000000000000000000000000000000000 64
11001100101101000000000000000000000000000000000000000000 56

Alternatively, you can follow this traditional solution like you attempted.

Strings in java are immutable. So instead you should create a StringBuilder object and keep copying every character, except 8th character.

For correctly counting every 8th character, initialize your for loop index run from 1 rather than 0, like in this code, which will eradicate every 8th character effectively where you wanted to do if (i%8==0)

public static void main(String[] args) {
    String str = "1100110001011011000000000000000000000000000000000000000000000000";
    StringBuilder sb = new StringBuilder();

    System.out.println(str + " " + str.length());

    for (int i = 1; i <= str.length(); i++) {
        // Every 8th element should be discarded

        if (i % 8 == 0) {
            // str.deleteCharAt(i);
        } else {
            sb.append(str.charAt(i-1));
        }
    }

    System.out.println(sb.toString() + " " + sb.length());
}

And this gives following output,

1100110001011011000000000000000000000000000000000000000000000000 64
11001100101101000000000000000000000000000000000000000000 56

You can verify here where only every 8th character is gone in this output.

The main problem with your code is that you don't adjust i when removing characters.

Let's visualize that. You want to remove the following marked characters ("every 8th element"):

1100110001011011000000000000000000000000000000000000000000000000
       ^       ^       ^       ^       ^       ^       ^       ^

Now we're at i = 7 and remove that character, but because you don't adjust i accordingly the markers keep the same:

110011001011011000000000000000000000000000000000000000000000000
       ^       ^       ^       ^       ^       ^       ^       ^

Let's do that for 1 = 15 to i = 55 :

11001100101101100000000000000000000000000000000000000000000000  //i = 15
1100110010110110000000000000000000000000000000000000000000000   //i = 23
110011001011011000000000000000000000000000000000000000000000    //i = 31
11001100101101100000000000000000000000000000000000000000000     //i = 39
1100110010110110000000000000000000000000000000000000000000      //i = 47
110011001011011000000000000000000000000000000000000000000       //i = 55   
       ^       ^       ^       ^       ^       ^       ^       ^

As you can see, all but the last marker point to a valid character but you won't reach i = 63 because after the first time you remove a character there only are 63 left in the string and thus a max index of 62.

That's why your resulting string has 57 instead of 56 characters, the last "remove" operation doesn't run (and the others except the first remove the wrong elements).

To fix that iterate backwards, ie from i = str.length() - 1 to i = 0 . Then you can remove every element where (i + 1) % 8 == 0 .

Alternatively, as I said in my comment, use a regex: String shortened = str.replaceAll( "(.{7}).", "$1" );

This will match any sequence of 7 characters followed by another (8th) character and replaces that with the first group of 7 (thus skipping the 8th).

String doesn't have a deleteCharAt() method. If it did, it would return the update string, since String is immutablem so code would have had to be str = str.deleteCharAt(i); .

You could use StringBuilder instead, since it does have a deleteCharAt() method.

To delete every 8th character, start at the end. That way index values are unaffected by already deleted characters, which is your current problem.

String str = "1100110001011011000000000000000000000000000000000000000000000000";
System.out.println(str + " " + str.length());

StringBuilder buf = new StringBuilder(str);
for (int i = (buf.length() - 1) / 8 * 8; i >= 0; i -= 8)
    buf.deleteCharAt(i);
str = buf.toString();
System.out.println(str + " " + str.length());

Output

1100110001011011000000000000000000000000000000000000000000000000 64
10011001011011000000000000000000000000000000000000000000 56

UPDATE

The above code deletes the 1st, 9th, 17th, ... character, ie characters at index 0, 8, 16, ..., which is in accordance with "remove every 8th char" and "check if i % 8 == 0" mentioned in the question.

If code should delete the 8th, 16th, 24th, ... character, ie characters at index 7, 15, 23, ..., then change initialization of i as follows:

for (int i = (buf.length() - 8) & ~7 | 7; i >= 0; i -= 8)
    buf.deleteCharAt(i);

Output

1100110001011011000000000000000000000000000000000000000000000000 64 11001100101101000000000000000000000000000000000000000000 56

The problem is that Strings are starting with 0. Therefore the 8th element has the index 7 and has to be removed as well, which you don't do in your loop. I'd write it like that (but noting that this might not be the most elegant solution):

public static void main(String[] args)
{
    String str = "1100110001011011000000000000000000000000000000000000000000000000";

    System.out.println(str + " " + str.length());
    int idx = 0;

    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < str.length(); i++) {
        idx++;
        if (idx == 8) {
            idx = 0;
            continue;
        }
        sb.append(str.charAt(i));
    }

    System.out.println(sb.toString() + " " + sb.length());

}

Outputs:

1100110001011011000000000000000000000000000000000000000000000000 64
11001100101101000000000000000000000000000000000000000000 56

An alternative way is using substring() method.

substring(int beginIndex, int endIndex) Returns a new string that is a substring of this string.

In every turn add 7 chars of the string to the new string and skip the 8th element of the string: sb.append(str.substring(start, start+7));

In first turn:

str.substring(0, 7) -> "1100110"
start += 8; -> start = 8; 

In second turn:

str.substring(8, 15) -> "0101101"
start += 8; -> start = 23; 

...

So the 8th element/the element has the index 7 ("0") has been skipped.

    String str = "1100110001011011000000000000000000000000000000000000000000000000";

    int length = str.length();
    int start = 0;

    StringBuilder sb = new StringBuilder();

    while((start+7)<length) {
        sb.append(str.substring(start, start+7));
        start += 8;
    }
    if(start<length) {
        sb.append(str.substring(start, length));
    }

    System.out.println(sb + " " + sb.length());
    System.out.println(str + " " + str.length());

Output:

11001100101101000000000000000000000000000000000000000000 56
1100110001011011000000000000000000000000000000000000000000000000 64

Assuming that the string does not contain the char with ASCII value 0, convert the string to a char array and change every 8th char with the char with ASCII value 0, then reconstruct the string and replace all chars with ASCII value 0 with "":

    String str = "0123456701234567012345670123456701234567012345670123456701234567";
    System.out.println("initial = " + str);

    char[] array = str.toCharArray();
    for (int i = 7; i < array.length; i = i + 8) {
        array[i] = 0;
    }

    str = String.valueOf(array).replace(String.valueOf(Character.toChars(0)), "");
    System.out.println("final   = " + str);

will print:

initial = 0123456701234567012345670123456701234567012345670123456701234567
final   = 01234560123456012345601234560123456012345601234560123456

Since StringBuilder::deleteCharAt changes the size of the underlying sequence, you need to process the target string in reverse order.

This solution is based on streams.

// create target string
String s = Stream.generate(() -> IntStream.range(0, 10))
    .limit(10)
    .map(stream -> stream.mapToObj(Objects::toString).collect(Collectors.joining()))
    .collect(Collectors.joining());

StringBuilder sb = new StringBuilder(s);

// delete first element or not?
boolean removeFirst = false;
IntStream.range(removeFirst ? 0 : 1, s.length())
    .boxed()
    .sorted(Collections.reverseOrder()) // reverse number stream
    .filter(i -> i % 8 == 0) // only keep multiples of 8
    .forEach(sb::deleteCharAt);

System.out.println(s);
System.out.println(sb.toString());

This is the output it produces

0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789

123456790123457890123567890134567891234567901234578901235678901345678912345679012345789

The first element missing is the 8, then 6 (16), then the 4 (24), etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM