Linux против Windows File.delete()

У меня возникли проблемы с пониманием того, как Windows обрабатывает файлы по сравнению с Linux. Я пытаюсь удалить запись в файле, который я использую в качестве "базы данных" (это просто обычный файл). Java, к сожалению, не предлагает никакого способа сделать это, поэтому мне нужно скопировать все записи, кроме той, которую я хочу удалить, во временный файл, удалить старую базу данных, создать новый пустой файл базы данных, скопировать содержимое временного файла в новый файл базы данных и завершить, удалив временный.

Вот код для моей функции:

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

    }

}

Этот код (за исключением "\r\n", который в Linux только "\n") отлично работает в Linux, но в Windows я обнаружил, что когда я активирую обработчик событий для удаления записи, программа только добавит дополнительные записи. После отладки я обнаружил, что вызов database.delete() на самом деле не удаляет файл базы данных, а вызов temp.delete() удаляет временный файл (как и положено). Я обнаружил, что это странно, поэтому я проверил права доступа к файлу, и они настроены на "чтение / запись". Я попробовал следующее исправление, которое нашел в интернете:

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

Но это не сработало. Я не могу думать ни о чем другом, что могло бы случиться.

2 ответа

Решение

AFAIK, ни одна ОС не поддерживает удаление части файла (кроме конца)

Вы не можете удалить файл, который вы открыли, поэтому вы должны быть уверены, что закрыли его везде, но вы можете создать временный файл и переименовать его как исходный. (Нет необходимости копировать его обратно)

Вот как я мог бы написать это

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

Что касается вашего \r\n против \n выпускать, использовать System.getProperty("line.separator") (или Java 7 System.lineSeparator()), чтобы определить вашу последовательность символов конца строки.

Убедитесь, что вы закрыли файл везде. Попробуйте использовать Path класс для чтения файла. Я не знаю, почему вы не можете удалить его в настоящее время, но, возможно, это решит проблему.

Кроме того, вы должны использовать следующую структуру для while цикл (чтобы избежать использования break):

while (endOfFileFlag == false) {

    line = databaseReader.readLine();

    if (line == null)
        endOfFileFlag = true;
    else if ( !line.contains(entryToRemove))
        tempWriter.write(line + "\r\n");

}
Другие вопросы по тегам