javadelete-fileplatform-specific

Linux vs. Windows File.delete()


I'm having a bit of trouble understanding how Windows handles files compared to Linux. I'm trying to remove an entry in a file that I'm using as a "database" (it's just a regular file). Java unfortunately offers no method for doing this, so I have to copy all the entries except the one I want to delete into a temporary file, delete the old database, create a new empty database file, copy the contents of the temporary file into the new database file, and finish off by deleting the temporary.

Here's the code for my function:

private void removeSelectedItem(String entryToRemove) {

    //original database file
    File database = new File("catalog");

    //all entries except selected will be written here first
    File temp = new File("temp");

    boolean endOfFileFlag = false;
    String line = "";

    try {

        //used for reading original database file
        BufferedReader databaseReader =
            new BufferedReader(new FileReader(database));

        //used for writing to newly-created temp file
        BufferedWriter tempWriter =
            new BufferedWriter(new FileWriter(temp, true));

        /*
         * Read original database line by line and test to see if the line
         * has the course prefix and id. If it does, it won't be written to
         * the temp file.
         */
        while (endOfFileFlag == false) {

            line = databaseReader.readLine();

            /*
             * This code is ugly. If possible, this check needs to be
             * made in the conditions of the while loop.
             */
            if (line == null) {

                endOfFileFlag = true;
                break;

            }

            //tests to see if the line is to be removed
            if ( !line.contains(entryToRemove))
                tempWriter.write(line + "\r\n");

        }

        endOfFileFlag = false; //reset this for the next loop
        databaseReader.close(); //database will be deleted
        tempWriter.close(); //temp file is written
        database.delete(); //delete this to create a new updated one below

        database.createNewFile();

        //writes to the new database
        BufferedWriter databaseWriter =
                new BufferedWriter(new FileWriter(database, true));

        //reads from the temp file
        BufferedReader tempReader =
            new BufferedReader(new FileReader(temp));

        //read temp line by line and add each line to a new catalog file
        while (endOfFileFlag == false) {

            line = tempReader.readLine();

            /*
             * This code is ugly. If possible, this check needs to be made
             * in the conditions of the while loop. Attempts thus far have
             */
            if(line == null){

                endOfFileFlag = true;
                break;

            }

            databaseWriter.write(line + "\r\n");

        }

        tempReader.close(); //temp will be deleted
        databaseWriter.close(); //new database has been written
        temp.delete(); //temp file no longer needed

        setUpInfo(); //update the lists with the new catalog info

    }catch(IOException e){

        System.out.println("removeSelectedItem()");
        e.printStackTrace();

    }

}

This code (for the exception of the "\r\n," which in Linux is only "\n") executes perfectly under Linux, but in Windows I found that when I would activate the event handler for removing an entry, the program would only add extra entries. After debugging I found that the call to database.delete() isn't actually deleting the database file, while the call to temp.delete() is deleting the temporary file (like it's supposed to). I found this odd so I checked the permissions of the file and they are set to "read/write." I tried the following fix I found on the internet:

endOfFileFlag = false; //reset this for the next loop
databaseReader.close(); //database will be deleted
tempWriter.close(); //temp file is written
database.delete(); //delete this to create a new updated one below
//new code
database = null;
System.gc();

But it didn't work. I can't think of anything else that could be happening.


Solution

  • AFAIK, No OS supports deleting part of a file (except the end)

    You can't delete a file you have open so you have to be sure you closed it everywhere, but you can create a temporary file and rename it as the original. (No need to copy it back)

    Here is how I might write it

    public static void removeLine(String filename, String line) {
        File from = new File(filename);
        File tmp = new File(filename + ".tmp");
        PrintWriter pw = null;
        BufferedReader br = null;
        try {
            pw = new PrintWriter(tmp);
            br = new BufferedReader(new FileReader(from));
            boolean found = false;
            for (String line2; (line2 = br.readLine()) != null; )
                if (line2.equals(line))
                    found = true;
                else
                    pw.println(line2);
            pw.close();
            br.close();
            if (found) {
                from.delete();
                tmp.renameTo(from);
            } else {
                tmp.delete();
            }
        } catch (IOException e) {
            // log error.
            try { if (br != null) br.close(); } catch (IOException ignored) { }
            if (pw != null) pw.close();
        }
    }