Как на самом деле работает "попытка с ресурсами" в контексте этой программы, которая удаляет все файлы и папки ниже заданного узла каталога

Эта программа удаляет все файлы и папки под данным узлом.

*РЕДАКТИРОВАТЬ*

У меня есть следующая "тестовая" структура каталогов на диске K:

Folder K:\garbage\
f_00000b (file)
f_00000f ( " )
f_0000b3 ( " )
dir1    [FOLDER]

Folder K:\garbage\dir1\
abc.pdf (file)
b12.pdf (file)
b36.pdf (file)
dir2   [FOLDER]

Folder K:\garbage\dir1\dir2\
A.pdf   (file)
a1.pdf  (file)
A2.pdf  (file)

*КОНЕЦ РЕДАКТИРОВАНИЯ*

Программа работает, потому что я наткнулся на "попробуй с ресурсами", линия, окруженная всеми ///////////////////////// будучи "ресурсом",

import java.io.IOError;
import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import javax.swing.JOptionPane;

public class CreatingDirectories {

    public static void main(String[] args) {

      deleteEverythingBelowThisNode(Paths.get("K:/garbage/dir1/dir2"));
    }

    static void deleteAllFilesIn(String ps){

      Path p = Paths.get(ps);

      try ////////////////////////////////////////////////////////////////
         (DirectoryStream<Path> paths = Files.newDirectoryStream(p)) /////
      { //////////////////////////////////////////////////////////////////

        for(Path q: paths)
          Files.delete(q);

      }catch(NotDirectoryException e){
        System.out.println("Not directory: " + p.toString() + "--" + e);
      }catch(DirectoryNotEmptyException e){
        System.out.println("Not empty: " + p.toString() + "--" + e);
      }
      catch(IOException e){
        System.out.println("IO: " + p.toString() + "--" + e);
      }
      catch(IOError e){
        System.out.println("IO ERROR: " + e);
      }
    }

    static void deleteEverythingBelowThisNode(Path p){

      String            sep        = p.getFileSystem().getSeparator();
      ArrayList<String> pathPieces = new ArrayList<>() ;
      String []         paths      = new String[p.getNameCount()];

      for(int i = 0 ; i < p.getNameCount() ; i++){
        pathPieces.add(p.getName(i).toString());
        paths[i] = p.getRoot().toString();
      }

      for(int i = 0; i < p.getNameCount() ; i++)
        for(int k = 0; k <= i; k++)
          paths[i] += pathPieces.get(k) + sep;

      for(int k = p.getNameCount() - 1; k >= 0; k--)
        deleteAllFilesIn(paths[k]);
    }
}

Я понимаю, что необходимо "попробовать с ресурсами": программа работает с ним, а не без него.

Но я не понимаю, почему, и я не знаю, как это решило исходную проблему, которую я сейчас опишу.

Первоначально я располагал "ресурсы" над блоком try, как это, что кажется совершенно естественным:

      DirectoryStream<Path> paths = Files.newDirectoryStream(p);
      try {
           for...

Со структурой программы в остальном идентичной, за исключением перемещения этой строки, как показано выше, все файлы и подпапки были успешно удалены из папки, но DirectoryNotEmptyException был брошен. Проводник Windows подтвердил, что каталог был пуст после завершения программы из-за исключения.

Почему исключение было брошено в пустой каталог?

Изо рта лошади: "Оператор try-with-resources... объявляет... объект, который... закрывается в конце оператора".

Закрытие происходит в конце оператора, то есть в конце цикла. Как исключение не произошло даже с try-with-resources?

Как и сейчас, после прохождения всего узла все, что находится под ним, было удалено.

Итак, что же на самом деле сделали try-with-resources, чтобы разрешить удаление пустой папки, которую нельзя удалить без try-with-resources?

Это не похоже на глупые вопросы или тривиальную ситуацию.

Сделал DirectoryNotEmptyException на самом деле происходит в любом случае, но попытка с ресурсами как-то справляется? Я не могу поверить, что спрашиваю об этом, поскольку это кажется глупым вопросом, но что на самом деле произошло, чтобы заставить программу работать так, как ожидалось?

3 ответа

Решение

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

Таким образом, если вы не используете оператор try-with-resources для закрытия потока каталога, у вас все равно будут открыты подкаталоги в тот момент, когда вы пытаетесь удалить файлы в родительском каталоге, и эта попытка находится в подкаталоге, который все еще открыть не удастся. Поскольку вы игнорируете исключения (вы просто распечатываете их), последующая попытка удалить родительский каталог также потерпит неудачу с DirectoryNotEmptyException так как вы не удалили все подкаталоги.

Вы можете проверить, так ли это на самом деле. Если вы не используете try-with-resources, убедитесь, что вы явно закрыли поток каталога после удаления всех файлов в каталоге (используя paths.close();)

Это должно иметь тот же эффект, что и блок try-with-resources (если не возникает исключение - чтобы гарантировать точно такое же поведение, что и в try-with-resources, вам нужно поставить paths.close(); в finally блок).

Эрвин объясняет ваш вопрос, но у вас также есть довольно серьезная проблема в конце вашего списка, по адресу:

   for(int k = p.getNameCount() - 1; k >= 0; k--)
        deleteAllFilesIn(paths[k]);

Вы создаете список частей пути, поэтому, например, части там будут:

  • к: / мусора /dir1/dir2
  • к: / мусора / dir1
  • к: / мусора
  • K: /

Это означает, что вы в конце концов попытаетесь удалить все в k:. Он попытается удалить все в k:\ (все файлы; он потерпит неудачу в целом, если есть какие-либо непустые подкаталоги).

Предполагая, что вы хотите удалить только файлы самого низкого уровня, вы, вероятно, хотите изменить deleteEverythingBelowThisNode() функция.

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

Во всяком случае, вот deleteAllFilesBelowThisNode с предложением @ Эрвина.

static void deleteAllFilesBelowThisNode(String ps) throws IOException{

  Path p = Paths.get(ps);

  try
  {
    paths = Files.newDirectoryStream(p);

    for(Path q: paths){

      if(JOptionPane.showConfirmDialog(null,"Deleting " + q.toString(),"",
           JOptionPane.OK_CANCEL_OPTION) 
        != JOptionPane.OK_OPTION)
                                  System.exit(9);

      Files.delete(q);
      System.out.println(q.toString() + " deleted");
    }
  }
  finally{
    JOptionPane.showMessageDialog(null,"AHA!");
    paths.close();
  }
}

Я добавил "АГА!" потому что я наконец понял, что происходит.

*РЕДАКТИРОВАТЬ*

И я вынул все, что так гордо добавил вчера, потому что я снова чувствую себя таким же плотным, как ртуть. Планета, металл, машина... whatevvvvvvvvvvs.

Я забыл, что было три отдельных звонка DeleteAllFilesBelowThisNode, который действительно смутил меня. Черт возьми, это МОЯ чертова программа, но... лес для деревьев и все такое. Не включая всю программу, я обманул меня. Одурачил меня хорошо. Дерн хорошо.

Я на самом деле не дебил.

*КОНЕЦ РЕДАКТИРОВАНИЯ

Но я оставил это:

СПАСИБО, ERWIN!

*ДРУГОЕ РЕДАКТИРОВАНИЕ*

Вот вывод для понимания:

Delete all entries beneath K:\garbage\dir1\dir2\
  deleting K:\garbage\dir1\dir2\A.pdf ... deleted
  deleting K:\garbage\dir1\dir2\a1 and 2 ver 2.pdf ... deleted
  deleting K:\garbage\dir1\dir2\A1 and 2.pdf ... deleted
K:\garbage\dir1\dir2\ closed by finally. ----- THUS DELETEABLE

Delete all entries beneath K:\garbage\dir1\
  deleting K:\garbage\dir1\abc.pdf ... deleted
  deleting K:\garbage\dir1\b12.pdf ... deleted
  deleting K:\garbage\dir1\b36.pdf ... deleted
  deleting K:\garbage\dir1\dir2 ... ***DELETED***
K:\garbage\dir1\ closed by finally. ----- THUS DELETEABLE


Delete all entries beneath K:\garbage\
  deleting K:\garbage\dir1 ... ***DELETED***
  deleting K:\garbage\f_00000b ... deleted
  deleting K:\garbage\f_00000f ... deleted
  deleting K:\garbage\f_0000b3 ... deleted
K:\garbage\ closed by finally.

Вот вся программа:

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import javax.swing.JOptionPane;

public class CreatingDirectories {

  static void deleteAllFilesBelowThisNode(String ps) throws IOException{

    DirectoryStream<Path> paths = null;

    try
    {
      paths = Files.newDirectoryStream(Paths.get(ps));

      for(Path q: paths){
                         System.out.print("deleting " + q.toString() + " ... ");
        if(JOptionPane.showConfirmDialog(null,"Deleting " + q.toString(),"",
             JOptionPane.OK_CANCEL_OPTION) 
          != JOptionPane.OK_OPTION)
                                                               System.exit(9);
        Files.delete(q);
                                                  System.out.println("deleted");
      }
    }
    finally{  

      paths.close();         
      System.out.println("\n" + ps + " closed by finally.\n");
    }
  }

  static void iterativelyDeleteFoldersFromHereUpToRoot(Path p) throws IOException{

    String            sep        = p.getFileSystem().getSeparator();
    ArrayList<String> pathPieces = new ArrayList<>() ;
    String []         paths      = new String[p.getNameCount()];

    for(int i = 0 ; i < p.getNameCount() ; i++){

      pathPieces.add(p.getName(i).toString());

      paths[i] = p.getRoot().toString();
    }

    for(int i = 0; i < p.getNameCount() ; i++)
      for(int k = 0; k <= i; k++)
                                  paths[i] += pathPieces.get(k) + sep;

    for(int k = p.getNameCount() - 1; k >= 0; k--){

      System.out.println("\nDelete all entries beneath " + paths[k].toString());

      deleteAllFilesBelowThisNode(paths[k]);
    }
  }

  public static void main(String[] args) throws IOException {

    iterativelyDeleteFoldersFromHereUpToRoot(Paths.get("K:/garbage/dir1/dir2"));
  }
}
Другие вопросы по тегам