Find a line in a file and remove it

Viewed 294480

I'm looking for a small code snippet that will find a line in file and remove that line (not content but line) but could not find. So for example I have in a file following:

myFile.txt:

aaa
bbb
ccc
ddd

Need to have a function like this: public void removeLine(String lineContent), and if I pass removeLine("bbb"), I get file like this:

myFile.txt:

aaa
ccc
ddd
17 Answers

Here you go. This solution uses a DataInputStream to scan for the position of the string you want replaced and uses a FileChannel to replace the text at that exact position. It only replaces the first occurrence of the string that it finds. This solution doesn't store a copy of the entire file somewhere, (either the RAM or a temp file), it just edits the portion of the file that it finds.

public static long scanForString(String text, File file) throws IOException {
    if (text.isEmpty())
        return file.exists() ? 0 : -1;
    // First of all, get a byte array off of this string:
    byte[] bytes = text.getBytes(/* StandardCharsets.your_charset */);

    // Next, search the file for the byte array.
    try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {

        List<Integer> matches = new LinkedList<>();

        for (long pos = 0; pos < file.length(); pos++) {
            byte bite = dis.readByte();

            for (int i = 0; i < matches.size(); i++) {
                Integer m = matches.get(i);
                if (bytes[m] != bite)
                    matches.remove(i--);
                else if (++m == bytes.length)
                    return pos - m + 1;
                else
                    matches.set(i, m);
            }

            if (bytes[0] == bite)
                matches.add(1);
        }
    }
    return -1;
}

public static void replaceText(String text, String replacement, File file) throws IOException {
    // Open a FileChannel with writing ability. You don't really need the read
    // ability for this specific case, but there it is in case you need it for
    // something else.
    try (FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ)) {
        long scanForString = scanForString(text, file);
        if (scanForString == -1) {
            System.out.println("String not found.");
            return;
        }
        channel.position(scanForString);
        channel.write(ByteBuffer.wrap(replacement.getBytes(/* StandardCharsets.your_charset */)));
    }
}

Example

Input: ABCDEFGHIJKLMNOPQRSTUVWXYZ

Method Call:

replaceText("QRS", "000", new File("path/to/file");

Resulting File: ABCDEFGHIJKLMNOP000TUVWXYZ

This solution reads in an input file line by line, writing each line out to a StringBuilder variable. Whenever it encounters a line that matches what you are looking for, it skips writing that one out. Then it deletes file content and put the StringBuilder variable content.

public void removeLineFromFile(String lineToRemove, File f) throws FileNotFoundException, IOException{
    //Reading File Content and storing it to a StringBuilder variable ( skips lineToRemove)
    StringBuilder sb = new StringBuilder();
    try (Scanner sc = new Scanner(f)) {
        String currentLine;
        while(sc.hasNext()){
            currentLine = sc.nextLine();
            if(currentLine.equals(lineToRemove)){
                continue; //skips lineToRemove
            }
            sb.append(currentLine).append("\n");
        }
    }
    //Delete File Content
    PrintWriter pw = new PrintWriter(f);
    pw.close();

    BufferedWriter writer = new BufferedWriter(new FileWriter(f, true));
    writer.append(sb.toString());
    writer.close();
}

Super simple method using maven/gradle+groovy.

public void deleteConfig(String text) {
    File config = new File("/the/path/config.txt")
    def lines = config.readLines()
    lines.remove(text);
    config.write("")
    lines.each {line -> {
        config.append(line+"\n")
    }}
}

Old question, but an easy way is to:

  • Iterate through file, adding each line to an new array list
  • iterate through the array, find matching String, then call the remove method.
  • iterate through array again, printing each line to the file, boolean for append should be false, which basically replaces the file

This solution uses a RandomAccessFile to only cache the portion of the file subsequent to the string to remove. It scans until it finds the String you want to remove. Then it copies all of the data after the found string, then writes it over the found string, and everything after. Last, it truncates the file size to remove the excess data.

public static long scanForString(String text, File file) throws IOException {
    if (text.isEmpty())
        return file.exists() ? 0 : -1;
    // First of all, get a byte array off of this string:
    byte[] bytes = text.getBytes(/* StandardCharsets.your_charset */);

    // Next, search the file for the byte array.
    try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) {

        List<Integer> matches = new LinkedList<>();

        for (long pos = 0; pos < file.length(); pos++) {
            byte bite = dis.readByte();

            for (int i = 0; i < matches.size(); i++) {
                Integer m = matches.get(i);
                if (bytes[m] != bite)
                    matches.remove(i--);
                else if (++m == bytes.length)
                    return pos - m + 1;
                else
                    matches.set(i, m);
            }

            if (bytes[0] == bite)
                matches.add(1);
        }
    }
    return -1;
}

public static void remove(String text, File file) throws IOException {
    try (RandomAccessFile rafile = new RandomAccessFile(file, "rw");) {
        long scanForString = scanForString(text, file);
        if (scanForString == -1) {
            System.out.println("String not found.");
            return;
        }
        long remainderStartPos = scanForString + text.getBytes().length;
        rafile.seek(remainderStartPos);
        int remainderSize = (int) (rafile.length() - rafile.getFilePointer());
        byte[] bytes = new byte[remainderSize];
        rafile.read(bytes);
        rafile.seek(scanForString);

        rafile.write(bytes);
        rafile.setLength(rafile.length() - (text.length()));
    }
}

Usage:

File Contents: ABCDEFGHIJKLMNOPQRSTUVWXYZ

Method Call: remove("ABC", new File("Drive:/Path/File.extension"));

Resulting Contents: DEFGHIJKLMNOPQRSTUVWXYZ

This solution could easily be modified to remove with a certain, specifiable cacheSize, if memory is a concern. This would just involve iterating over the rest of the file to continually replace portions of size, cacheSize. Regardless, this solution is generally much better than caching an entire file in memory, or copying it to a temporary directory, etc.

Related