简体   繁体   English

“try-with-resources”实际上如何在此程序的上下文中工作,删除给定目录节点下的所有文件和文件夹

[英]How does “try-with-resources” actually work in the context of this program that deletes all files and folders below a given directory node

This program deletes all files and folders beneath a given node. 该程序删除给定节点下的所有文件和文件夹。

* EDIT * * 编辑 *

I have the following "test" directory structure on drive K: 我在驱动器K上有以下“test”目录结构:

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)

* END EDIT * * 结束编辑 *

Program works because I stumbled onto "try-with-resources", the line surrounded with all the ///////////////////////// being "the resource". 程序有效,因为我偶然发现了“尝试资源”,这条线被所有的/////////////////////////所包围,成为“资源” 。

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

I understand that "try-with-resources" is necessary: program works with it, not without it. 我知道“尝试使用资源”是必要的:程序可以使用它,而不是没有它。

But I don't understand why and I don't know how it solved the original problem, which I now describe. 但我不明白为什么,我不知道它是如何解决原来的问题,我现在描述。

I originally had situated the "resources" above the try-block, like this, which seems perfectly natural: 我最初将“资源”放在try-block之上,就像这样,看起来非常自然:

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

With the program structure otherwise identical except for moving that one line as shown above, all files and subfolder had been successfully deleted from a folder, but DirectoryNotEmptyException was thrown. 除了如上所示移动那一行之外,程序结构相同,所有文件和子文件夹都已成功从文件夹中删除,但抛出了DirectoryNotEmptyException Windows Explorer confirmed that the directory was empty after the program terminated because of the exception. 由于异常,Windows资源管理器确认程序终止后该目录为空。

Why was the exception thrown on an empty directory? 为什么在空目录上抛出异常?

From the horse's mouth , "The try-with-resources statement ... declares ... an object that ... is closed at the end of the statement." 马的口中 ,“尝试资源声明......声明......一个......在声明结束时关闭的对象。”

Closing happens at the end of the statement, so at the end of the loop. 结束发生在语句的末尾,所以在循环结束时。 How did the exception not occur even with try-with-resources? 即使使用try-with-resources,异常怎么办?

As it now is, after looping through the entire node, everything beneath it has been deleted. 就像现在一样,在循环整个节点之后,它下面的所有内容都被删除了。

So what did try-with-resources actually do to enable deleting an empty folder that could not be deleted without try-with-resources? 那么try-with-resources实际上做了什么来启用删除没有try-with-resources无法删除的空文件夹?

These don't seem like stupid questions or a trivial situation. 这些似乎不是愚蠢的问题或琐碎的情况。

Did a DirectoryNotEmptyException actually occur anyway, but try-with-resources somehow handled it? 无论如何都发生了DirectoryNotEmptyException ,但尝试使用资源以某种方式处理它? I can't believe I'm asking that, since it does seem like a stupid question, but what actually happened to make the program run as expected? 我不敢相信我问这个,因为它确实看起来像一个愚蠢的问题,但实际上是什么让程序按预期运行?

On Windows, you cannot delete a file or directory that is still open. 在Windows上,您无法删除仍处于打开状态的文件或目录。 (On Unix, on the other hand, this is not a problem - the file will be deleted from the directory structure when you delete it and from disk when you close it. But that's on Unix.) (另一方面,在Unix上,这不是问题 - 当你删除它时,文件将从目录结构中删除。当你关闭它时,文件将从磁盘中删除。但是这是在Unix上。)

So if you don't use the try-with-resources statement to close the directory stream, you will still have subdirectories open at the moment that you try to delete the files in the parent directory, and that attempt to the subdirectory that is still open will fail. 因此,如果您不使用try-with-resources语句来关闭目录流,那么在您尝试删除父目录中的文件时,仍会打开子目录,并且尝试到仍然存在的子目录打开会失败。 Since you ignore exceptions (you just print them), the subsequent attempt to delete the parent directory will also fail with a DirectoryNotEmptyException since you didn't delete all the subdirectories. 由于您忽略了异常(您只是打印它们),因此删除父目录的后续尝试也将因DirectoryNotEmptyException失败,因为您没有删除所有子目录。

You can verify if this is really the case. 您可以验证是否确实如此。 When you do not use try-with-resources, make sure that you explicitly close the directory stream after you delete all files in the directory (using paths.close(); ) 如果使用try-with-resources,请确保在删除目录中的所有文件后显式关闭目录流(使用paths.close();

That should have the same effect as the try-with-resources block (unless an exception occurs - to guarantee exactly the same behavior as try-with-resources, you need to put paths.close(); in a finally block). 这应该与try-with-resources块具有相同的效果(除非发生异常 - 为了保证与try-with-resources完全相同的行为,您需要将paths.close();放在finally块中)。

Erwin explains your question, but you also have a fairly serious problem in the end of your listing, at: Erwin解释了你的问题,但你在列表的最后还有一个相当严重的问题:

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

You create a list of path parts, so for example, the parts in there would be: 您可以创建路径部件列表,例如,其中的部件将是:

  • k:/garbage/dir1/dir2 K:/垃圾/ DIR1 / DIR2
  • k:/garbage/dir1 K:/垃圾/ DIR1
  • k:/garbage K:/垃圾
  • k:/ K:/

That means you'll eventually try to delete everything in k:. 这意味着你最终会尝试删除k:中的所有内容。 It will try to delete everything at k:\\ (all files; it will fail overall if there are any non-empty subdirectories). 它会尝试删除k:\\中的所有内容(所有文件;如果有任何非空子目录,它将整体失败)。

Assuming you only want to delete the lowest-level files, you probably want to change the deleteEverythingBelowThisNode() function. 假设您只想删除最低级别的文件,您可能想要更改deleteEverythingBelowThisNode()函数。

(I hope it's OK to "answer my own question" to make the 'bottom line' of the thread show what wound up working. This way anyone who views the thread won't have to look hard for the solution.) (我希望“回答我自己的问题”可以让线程的“底线”显示出什么结果正常工作。这样任何观看线程的人都不必为解决方案而努力。)

Anyway, here's deleteAllFilesBelowThisNode with @Erwin's suggestion. 无论如何,这里是deleteAllFilesBelowThisNode建议的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();
  }
}

I added the "AHA!" 我加了“AHA!” because I FINALLY realized what's going on. 因为我终于意识到发生了什么。

* EDIT * * 编辑 *

And I took out all that I so proudly added yesterday because once again I find myself as dense as mercury. 我拿出了我昨天自豪地添加的所有内容,因为我再次发现自己像水银一样密集。 The planet, the metal, the car... whatevvvvvvvvvvs. 行星,金属,汽车...... whatevvvvvvvvvvs。

I'd forgotten that there were three separate calls to DeleteAllFilesBelowThisNode , which really confused the heck out of me. 我忘记了有三个单独调用DeleteAllFilesBelowThisNode ,这真的让我感到困惑。 Heck, it's MY dang program, but ... forest for trees, and all that. 哎呀,这是我的dang节目,但是...森林树木,以及所有这些。 By not including the entire program, I fooled me. 由于不包括整个程序,我欺骗了我。 Fooled me good. 愚弄我好。 Dern good. 德恩好。

I'm not really a moron. 我不是一个白痴。

* END EDIT * 结束编辑

But I left this: 但我离开了这个:

THANK YOU, ERWIN! 谢谢你,ERWIN!

* ANOTHER EDIT * * 另一个编辑 *

Here's output, for understanding: 这是输出,用于理解:

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.

Here's entire program: 这是整个计划:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM