Why can’t I use Stringbuilder’s delete() method more than once in the same code block?

Asked

Viewed 43 times

0

I’m trying to erase an interval of content from a StringBuilder, but when using the method delete() more than once the program gives error.

Specifically, I’m trying to delete the pseudo-code part of the resolution of some of the questions in a virtual book, leaving only the statement. There is a pattern at the start of pseudocodes, as they begin with Prog and ends with fimprog. Note the example below:

algoritmo 362
Criar um algoritmo que leia dois conjuntos de números inteiros, tendo cada
284 um 10 e 20 elementos, e apresente os elementos comuns aos conjuntos. Lembre-se de que os elementos podem se repetir, mas não podem aparecer repetidos na saída.
prog vetorl10

pseudocógigos...

fimprog

algoritmo 363
Criar um algoritmo que leia vários números inteiros e positivos. A leitura se en-
cerra quando encontrar um número negativo ou quando o vetor ficar completo.
Sabe-se que o vetor possui, no máximo, 10 elementos. Gerar e imprimir um vetor
onde cada elemento é o inverso do correspondente do vetor original.
prog vetorl11

pseudocógigos...

fimprog

algoritmo364...

Code steps:

  • The program read a file;
  • Puts content in a StringBuilder;
  • The program takes the initial index of the word "Prog" and "fimprog";
  • The program calls the delete method and deletes the contents of the defined ranges;
  • The program takes the next index of the word "Prog" and "fimprog" again and succeeds, but at the time of deleting the next interval gives error.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.Character.Subset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BuscarPadroes {

    public static void main(String[] args) {

        String path = "c:\\socket\\Lista_vetor.txt";

        String line;
        StringBuilder conteudo = new StringBuilder();
        int cont = 0;
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {

            while (br.ready()) {
                line = br.readLine();
                cont++;
                conteudo.append(line + "\n");
            }
            
            
            int start = conteudo.indexOf("prog");
            int end = conteudo.indexOf("fimprog");
            conteudo.delete(start, end);
            
            start = conteudo.indexOf("prog ");
            end = conteudo.indexOf("fimprog");
            conteudo.delete(start, end);

        } catch (IOException erro) {
            System.out.println("Error: " + erro.getMessage());
        }
    }
}

2 answers

0


There are several problems there. The first has already been indicated in another answer: indexOf returns the index where begins the occurrence of substring. That is, conteudo.indexOf("fimprog") will return the position where the string "fimprog" starts, which means that "fimprog" will not be removed in fact, because according to the documentation, the second index is not included (for example, delete(1, 4) deletes positions 1, 2 and 3). That is, "fimprog" is not removed, and the second time you search, it will be before "Prog", ie start will be greater than end - and the documentation says that in this case there is error.

Then you have to pick up 7 more positions ahead (or 8 if you want to eliminate line breaking as well), but still have other problems. Your code was made in a way that only searches for two occurrences of "Prog" and "fimprog", but what if you have more (or less - which includes the case of not having any)? What if you only have "Prog" without the respective "fimprog"? Etc...

Anyway, for a more general case, it would be better to make one loop. As long as you have "Prog" and "fimprog", you remove it. When you no longer have loop:

while (true) {
    int start = conteudo.indexOf("prog");
    if (start < 0) {
        // não tem prog, nem adianta continuar
        break;
    }
    int end = conteudo.indexOf("fimprog");
    if (end < 0) {
        // não tem fimprog, nem adianta continuar
        break;
    }
    if (start > end) {
        System.out.println("texto mal formado, prog está depois de fimprog");
        break; // texto mal formado, nem adianta continuar
    }
    // somar o tamanho de "fimprog", senão ela não é apagada
    conteudo.delete(start, end + 7);
}

Reversing the logic

But in fact, I think this is all unnecessary. Instead of playing the whole text for a String and then go out searching and removing some excerpts, wouldn’t it be better to simply do not add the snippets you do not want?

Assuming that "Prog" and "fimprog" are always at the beginning of the line, it would look like this:

boolean adiciona = true;
while (br.ready()) {
    line = br.readLine();
    if (line.startsWith("prog")) {
        adiciona = false;
    } else if (line.startsWith("fimprog")) {
        adiciona = true;
    } else if (adiciona) {
        conteudo.append(line + "\n");
    }
}

That is, if I find "Prog", I ignore all the lines from then on, until I find "fimprog" (and then I add the lines, until I find a "Prog" again). The detail is that in this case I am not checking if the file is badly formed (if you only have a "Prog" and do not have the respective "fimprog", for example, all the rest of the file after this "Prog" will be ignored). If there is such a possibility, then the first option above would be better (or, depending on the case, maybe it is the case to make a parser own to validate). But if it is "guaranteed" that the file is always well formed, I think better to do so instead of adding everything to then go removing.

  • 1

    Gosh, thank you very much. It was exactly the reverse logic that I tried to do, but I couldn’t and I decided to try it this way. Detail, I put two occurrences for testing and for lack of a better understanding of the index. I will read back the documentation of the method.

0

The problem is in the logic of your delete. The method indexOf return the word start index, so you need to consider the word size when passing to the method delete. If you do not, in the second iteration the word "fimprog" will be before the word "Prog", causing error.

int start = conteudo.indexOf("prog");
int end = conteudo.indexOf("fimprog") + "fimprog".length();
conteudo.delete(start, end);

For more details see the Stringbuilder class documentation: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/StringBuilder.html

  • Great. I understood the index method in another way, even reading the documentation. Thank you very much!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.