从字符串中删除每8个字符

时间:2018-11-19 16:58:52

标签: java string

我有一个字符串,我想对其进行迭代并删除每8个字符。我一直在尝试进行模运算,以检查i % 8 == 0。但是,由于我每隔8个char删除一次,因此字符串的长度减小了,因此无法执行该操作。

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

字符串的长度在开头64,在for循环57之后,应为56。

8 个答案:

答案 0 :(得分:1)

deleteCharAt中没有String方法,所以我想你是说StringBuilder

您可以反转for循环的方向,以便它从字符串的末尾开始:

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

通过从字符串末尾删除字符,沿着字符串移动时,要删除的字符的索引不再更改。

答案 1 :(得分:1)

为什么不使用正则表达式并通过两行这样的代码来实现它,

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

这给出了完全正确的输出,

1100110001011011000000000000000000000000000000000000000000000000 64
11001100101101000000000000000000000000000000000000000000 56

或者,您可以按照尝试的传统方法进行操作。

java中的字符串是不可变的。因此,您应该创建一个StringBuilder对象,并继续复制除第8个字符之外的每个字符。

要正确计数每个第8个字符,请像下面的代码一样,从1而不是0初始化for循环索引,它将有效地根除每8个字符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());
}

这给出了以下输出,

1100110001011011000000000000000000000000000000000000000000000000 64
11001100101101000000000000000000000000000000000000000000 56

您可以在此处验证此输出中仅第8个字符消失的地方。

答案 2 :(得分:1)

代码的主要问题是删除字符时您没有调整i

让我们想象一下。您要删除以下标记的字符(“第8个元素”):

1100110001011011000000000000000000000000000000000000000000000000
       ^       ^       ^       ^       ^       ^       ^       ^

现在我们在i = 7处并删除该字符,但是由于您没有相应地调整i,因此标记保持不变:

110011001011011000000000000000000000000000000000000000000000000
       ^       ^       ^       ^       ^       ^       ^       ^

让我们从1 = 15i = 55

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

如您所见,除最后一个标记外,所有标记均指向有效字符,但您不会到达i = 63,因为第一次删除字符后,字符串中仅剩下63个字符,因此最大索引为62。

这就是为什么您生成的字符串有57个字符而不是56个字符的原因,最后一个“删除”操作没有运行(除第一个以外,其他操作都删除了错误的元素)。

要解决该问题,请从i = str.length() - 1i = 0向后迭代。然后,您可以删除(i + 1) % 8 == 0中的每个元素。

或者,正如我在评论中所说,请使用正则表达式:String shortened = str.replaceAll( "(.{7}).", "$1" );

这将匹配任何7个字符的序列,然后是另一个(第8个)字符,并将其替换为第一个7组(因此跳过第8个)。

答案 3 :(得分:0)

String没有deleteCharAt()方法。如果这样做,它将返回更新字符串,因为String是不可变的,因此代码必须为str = str.deleteCharAt(i);

您可以改用StringBuilder,因为它确实具有deleteCharAt()方法。

要删除每8个字符,请从结尾开始。这样,索引值不受已删除的字符的影响,这是您当前的问题。

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

输出

1100110001011011000000000000000000000000000000000000000000000000 64
10011001011011000000000000000000000000000000000000000000 56

更新

上面的代码删除了第1、9、17,...个字符,即索引为0、8、16,...的字符,这与“每8个字符删除一次”和“检查i% 8 == 0“在问题中提到。

如果代码应删除第8、16、24,...个字符,即索引7、15、23,...处的字符,则按以下方式更改i的初始化:

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

输出

1100110001011011000000000000000000000000000000000000000000000000000000 64 11001100101101000000000000000000000000000000000000000000000000 56

答案 4 :(得分:0)

问题在于字符串以0开头。因此,第8个元素的索引为7,因此也必须将其删除,这在循环中不执行。我会这样写(但请注意,这可能不是最优雅的解决方案):

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

}

输出:

1100110001011011000000000000000000000000000000000000000000000000 64
11001100101101000000000000000000000000000000000000000000 56

答案 5 :(得分:0)

另一种方法是使用substring()方法。

  

substring(int beginIndex,int endIndex)返回一个新字符串,该字符串是   该字符串的子字符串。

在新字符串中依次添加字符串的7个字符,并跳过字符串的第8个元素:sb.append(str.substring(start, start+7));

第一轮:

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

第二回合:

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

...

第8个元素/索引为7 ("0")的元素已被跳过。

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

输出:

11001100101101000000000000000000000000000000000000000000 56
1100110001011011000000000000000000000000000000000000000000000000 64

答案 6 :(得分:0)

假定该字符串不包含ASCII值为0的char,请将字符串转换为char数组,并使用ASCII值为0的char更改每第8个char,然后重构该字符串并将ASCII值0的所有char替换为“”:

^XA
^POI
^LL600
^LS0
^BY2,2,59
^LH0,0
^FT100,400^BQN,2,10^FD##\(attendee!.id)^FS
^A0N,50,50^FO350,240^FD\(name)^FS
^A0N,40,40^FO350,290^FD\(attendee!.company)^FS
^XZ

将打印:

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

答案 7 :(得分:0)

由于StringBuilder::deleteCharAt会更改基础序列的大小,因此您需要以相反的顺序处理目标字符串。

此解决方案基于流。

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

这是它产生的输出

  

0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789

     

123456790123457890123567890134567891234567901234578901235678901345678912345679012345789

缺少的第一个元素是8,然后是6(16),然后是4(24),等等。