CodeGym /Java 博客 /随机的 /Java 文件,路径
John Squirrels
第 41 级
San Francisco

Java 文件,路径

已在 随机的 群组中发布
你好!今天我们将讨论使用文件和目录。您已经知道如何管理文件内容:我们为此专门上了很多课 :) 我想您会发现很容易记住一些用于这些目的的类。在今天的课程中,我们将专门讨论文件管理:创建、重命名等。在 Java 7 之前,所有这些操作都是使用 File执行的。你可以在这里阅读它。但是在 Java 7 中,该语言的创建者决定改变我们使用文件和目录的方式。发生这种情况是因为File类有几个缺点。例如,它没有copy()方法,该方法可以让您将文件从一个位置复制到另一个位置(看似必不可少的功能)。除此之外文件类有很多返回布尔值的方法。当出现错误时,此类方法返回 false。它不会抛出异常,因此很难识别错误并诊断其原因。在单个File类的位置,出现了 3 个类:PathsPathFiles。嗯,准确的说,Path是一个接口,而不是一个类。让我们弄清楚它们之间有何不同以及为什么我们需要它们中的每一个。让我们从最简单的开始:路径

路径

Paths是一个非常简单的类,只有一个静态方法:get()。创建它只是为了从传递的字符串或 URI 中获取Path对象。它没有其他功能。这是它在工作中的一个例子:

import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {

   public static void main(String[] args) {

       Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
   }
}
不是最复杂的课程,对吧?:) 嗯,我们也有这种Path类型。让我们弄清楚Path是什么以及为什么需要它:)

小路

Path大体上是File类的重新设计类比。它比File更容易使用。 首先,许多实用(静态)方法被取出并移至Files类。 其次,对Path接口的方法的返回值施加了顺序。在File类中,方法返回StringbooleanFile。弄清楚这件事并不容易。例如,有一个getParent()方法返回一个表示当前文件父路径的字符串。但也有一个getParentFile()方法,它返回相同的东西,但以File对象的形式!这显然是多余的。因此,在Path接口中, getParent()方法和其他处理文件的方法只返回一个Path对象。没有一堆选择——一切都很简单。 Path有哪些有用的方法? 以下是其中的一些以及它们如何工作的示例:
  • getFileName():从路径返回文件名;

  • getParent():返回当前路径的“父”目录(换句话说,目录树中紧靠上方的目录);

  • getRoot():返回“根”目录,即目录树顶部的目录;

  • startsWith() , endsWith():检查路径是否以传递的路径开始/结束:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           Path fileName = testFilePath.getFileName();
           System.out.println(fileName);
    
           Path parent = testFilePath.getParent();
           System.out.println(parent);
    
           Path root = testFilePath.getRoot();
           System.out.println(root);
    
           boolean endWithTxt = testFilePath.endsWith("Desktop\\testFile.txt");
           System.out.println(endWithTxt);
    
           boolean startsWithLalala = testFilePath.startsWith("lalalala");
           System.out.println(startsWithLalala);
       }
    }
    

    控制台输出:

    
    testFile.txt
    C:\Users\Username\Desktop
    C:\
    true
    false
    

    请注意endsWith()方法的工作原理。它检查当前路径是否以传递的路径结尾。具体是不是在路径中,而不是在传递的字符串中

    比较这两个调用的结果:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath.endsWith("estFile.txt"));
           System.out.println(testFilePath.endsWith("Desktop\\testFile.txt"));
       }
    }
    

    控制台输出:

    
    false
    true
    

    endsWith ()方法必须传递一个真正的路径,而不仅仅是一组字符:否则,结果将始终为 false,即使当前路径确实以该字符序列结尾(如 "estFile.txt 的情况) “在上面的例子中)。

    此外,Path有一组方法可以简化对绝对(完整)和相对路径的处理

让我们看看这些方法:
  • 如果当前路径是绝对路径,则boolean isAbsolute()返回 true:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath.isAbsolute());
       }
    }
    

    控制台输出:

    
    true
    
  • Path normalize():“规范化”当前路径,从中删除不必要的元素。您可能知道在流行的操作系统中符号“.”。(当前目录)和“..”(父目录)通常用于指定路径。例如,“ ./Pictures/dog.jpg ”表示当前目录下有一个“Pictures”文件夹,里面又包含一个“dog.jpg”文件。

    看这里。如果路径使用“.” 或 ".." 出现在您的程序中,normalize()方法将删除它们并生成不包含它们的路径:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
          
           Path path5 = Paths.get("C:\\Users\\Java\\.\\examples");
          
           System.out.println(path5.normalize());
          
           Path path6 = Paths.get("C:\\Users\\Java\\..\\examples");
           System.out.println(path6.normalize());
       }
    }
    

    控制台输出:

    
    C:\Users\Java\examples
    C:\Users\examples
    
  • Path relativize():计算当前路径和传递路径之间的相对路径。

    例如:

    
    import java.nio.file.Path;
    import java.nio.file.Paths;
    
    public class Main {
    
       public static void main(String[] args) {
    
           Path testFilePath1 = Paths.get("C:\\Users\\Users\\Users\\Users");
           Path testFilePath2 = Paths.get("C:\\Users\\Users\\Users\\Users\\Username\\Desktop\\testFile.txt");
    
           System.out.println(testFilePath1.relativize(testFilePath2));
       }
    }
    

    控制台输出:

    
    Username\Desktop\testFile.txt
    

Path方法的完整列表很长。您可以在Oracle 文档中找到它们。现在我们将继续考虑Files

文件

Files是一个实用程序类,它包含从File类中提取的静态方法。Files与ArraysCollections相当。不同之处在于它使用文件,而不是数组或集合:) 它专注于管理文件和目录。使用Files类的静态方法,我们可以创建、删除和移动文件和目录。这些操作是使用createFile()(对于目录, createDirectory())、 move()delete()方法执行的。以下是如何使用它们:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class Main {

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

       // Create a file
       Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
       System.out.println("Was the file created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       // Create a directory
       Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
       System.out.println("Was the directory created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));

       // Move the file from the desktop to the testDirectory directory. When you move a folder, you need to indicate its name in the folder!
       testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Did our file remain on the desktop?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       System.out.println("Has our file been moved to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));

       // Delete a file
       Files.delete(testFile1);
       System.out.println("Does the file still exist?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
   }
}
这里我们首先在桌面上 创建一个文件(Files.createFile()方法)。然后我们在同一位置创建一个文件夹(Files.createDirectory()方法)。之后,我们将文件(Files.move()方法)从桌面移动到这个新文件夹,最后删除文件(Files.delete()方法)。 控制台输出:

Was the file created successfully? 
true 
Was the directory created successfully? 
true
Did our file remain on the desktop? 
false 
Has our file been moved to testDirectory? 
true 
Does the file still exist? 
false
笔记:与接口的方法一样Path,类的许多方法都Files返回一个Path对象。该类的大多数方法Files也将Path对象作为输入。这里的Paths.get()方法将是您忠实的助手——好好利用它。还有什么有趣的地方Files?老File班真正缺的是copy()方法!我们在本课开始时谈到了它。现在是时候见到它了!

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;

public class Main {

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

       // Create a file
       Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
       System.out.println("Was the file created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       // Create a directory
       Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
       System.out.println("Was the directory created successfully?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));

       // Copy the file from the desktop to the testDirectory2 directory.
       testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Did our file remain on the desktop?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       System.out.println("Was our file copied to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
   }
}
控制台输出:

Was the file created successfully? 
true 
Was the directory created successfully? 
true 
Did our file remain on the desktop? 
true 
Was our file copied to testDirectory? 
true
现在您知道如何以编程方式复制文件了!:) 当然,该类Files不仅可以让您管理文件本身,还可以处理文件的内容。它有write()向文件写入数据的方法,还有读取数据的3种方法:read(), readAllBytes(), 和readAllLines() 最后一个我们将详细介绍。为什么是那个?因为它有一个非常有趣的返回类型:List<String>! 也就是说,它向我们返回文件中所有行的列表。当然,这使得处理文件内容变得非常方便,因为整个文件逐行显示,例如,可以使用普通循环显示在控制台上for

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {

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

       List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);

       for (String s: lines) {
           System.out.println(s);
       }
   }
}
控制台输出:

I still recall the wondrous moment: 
When you appeared before my sight, 
As though a brief and fleeting omen, 
Pure phantom in enchanting light.
超级方便!:) 这种能力出现在Java 7中。Stream API出现在Java 8中。它为Java增加了一些函数式编程的元素。包括更丰富的文件处理能力。假设我们有以下任务:找到所有以单词“As”开头的行,将它们转换为大写,并将它们显示在控制台上。在 Java 7 中使用该类的解决方案会是什么样子Files?是这样的:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import static java.nio.charset.StandardCharsets.UTF_8;

public class Main {

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

       List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);

       List<String> result = new ArrayList<>();

       for (String s: lines) {
           if (s.startsWith("As")) {
               String upper = s.toUpperCase();
               result.add(upper);
           }
       }

       for (String s: result) {
           System.out.println(s);
       }
   }
}
控制台输出:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
任务已完成,但您不认为对于这样一个简单的任务,我们的代码竟然有点……冗长吗?使用 Java 8 的 Stream API,解决方案看起来更优雅:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

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

       Stream<String> stream = Files.lines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"));

       List<String> result  = stream
               .filter(line -> line.startsWith("As"))
               .map(String::toUpperCase)
               .collect(Collectors.toList());
       result.forEach(System.out::println);
   }
}
我们获得了相同的结果,但代码更少!更何况,没有人能说我们失去了“可读性”。我认为您可以轻松地评论此代码的作用,即使不熟悉 Stream API 也是如此。简而言之,Stream是一系列元素,您可以对其执行各种操作。我们从该Files.lines()方法中获取一个 Stream 对象,然后对其应用 3 个函数:
  1. 我们使用该filter()方法仅从文件中选择以“As”开头的那些行。

  2. 我们使用该方法遍历所有选定的行map(),并将它们中的每一行都转换为大写。

  3. 我们使用该collect()方法将所有接收到的行收集到一个List.

我们得到相同的输出:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
现在让我们回到我们的面包和黄油,即文件 :) 我们今天要考虑的最后一个功能是遍历文件树。在现代操作系统中,文件结构通常看起来像一棵树:它有根和分支,分支可以有其他分支,等等。根和分支是目录。例如,目录“ С:// ”可能是根目录。它包括两个分支:“ C://Downloads ”和“ C://Users ”。这些分支中的每一个都有两个分支:“ C://Downloads/Pictures ”、“ C://Downloads/Video ”、“ C://Users/JohnSmith ”、“ C://Users/Pudge2005””。而这些分支依次有其他分支等等,这就是我们称其为树的原因。在 Linux 上,结构类似,但 / 目录是根目录现在 文件,路径 - 2假设我们需要从根目录开始, 遍历其所有文件夹和子文件夹,并找到具有某些特定内容的文件。我们将搜索包含“这是我们需要的文件!”这一行的文件,我们将获取“testFolder”文件夹,该文件夹位于桌面,作为根目录。以下是它的内容: 文件,路径 - 3level1-a 和 level1-b 文件夹还包含文件夹: 文件,路径 - 4文件,路径 - 5这些“二级文件夹”中没有文件夹,只有单个文件: 文件,路径 - 6文件,路径 - 7包含我们需要的内容的3个文件被特意赋予了解释性名称:FileWeNeed1.txt、FileWeNeed2.txt、FileWeNeed3.txt。这些正是我们需要使用 Java 查找的文件。我们如何做到这一点?一个非常强大的遍历文件树的方法可以帮助我们:Files.walkFileTree (). 这是我们需要做的。首先,我们需要一个FileVisitor. FileVisitor是一个特殊的接口,其中描述了遍历文件树的方法。特别是,这就是我们放置读取文件内容并检查它是否包含我们需要的文本的逻辑的地方。这是我们的FileVisitor样子:

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

public class MyFileVisitor extends SimpleFileVisitor<Path> {

   @Override
   public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

       List<String> lines = Files.readAllLines(file);
       for (String s: lines) {
           if (s.contains("This is the file we need")) {
               System.out.println("We found a file we need!");
               System.out.println(file.toAbsolutePath());
               break;
           }
       }

       return FileVisitResult.CONTINUE;
   }
}
在这种情况下,我们的类继承了SimpleFileVisitor. 这是一个实现 的类FileVisitor,我们只需要重写其中一个方法:visitFile()。在这里,我们定义需要对每个目录中的每个文件执行的操作。如果您需要更复杂的逻辑来遍历文件结构,您应该编写自己的FileVisitor. 您需要在该类中再实现 3 个方法:
  • preVisitDirectory():进入文件夹前执行的逻辑;

  • visitFileFailed():如果无法访问文件(无法访问或出于其他原因)时执行的逻辑;

  • postVisitDirectory():进入文件夹后执行的逻辑。

我们不需要执行任何此类逻辑,所以我们可以使用SimpleFileVisitor. 该方法内部的逻辑visitFile()非常简单:读取文件中的所有行,检查它们是否包含我们需要的内容,如果包含,则在控制台打印绝对路径。唯一可能给您带来困难的是这一行:

return FileVisitResult.CONTINUE;
其实,这很简单。这里我们只是简单地描述在文件被访问并执行了所有必要的操作之后程序应该做什么。在我们的例子中,我们想继续遍历树,所以我们选择了这个CONTINUE选项。 但是,或者,我们可能有一个不同的目标:不是查找所有包含“This is the file we need”的文件,而是只查找一个这样的文件。之后,程序应该终止。在这种情况下,我们的代码看起来完全一样,但不是 break 而是:

return FileVisitResult.TERMINATE;
好吧,让我们运行我们的代码,看看它是否有效。

import java.io.IOException;
import java.nio.file.*;

public class Main {

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

       Files.walkFileTree(Paths.get("C:\\Users\\Username\\Desktop\\testFolder"), new MyFileVisitor());
   }
}
控制台输出:

We found a file we need! 
C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt 
We found a file we need! 
C:\Users\Username\Desktop\testFolder\level1-a\level2-a-a\FileWeNeed2.txt 
We found a file we need! 
C:\Users\Username\Desktop\testFolder\level1-b\level2-b-b\FileWeNeed3.txt
出色的!有效!:) 您也可以接受这个小挑战:SimpleFileVisitor用一个普通的替换FileVisitor,覆盖所有 4 个方法,并为该程序提出您自己的目的。例如,您可以编写一个程序来记录其所有操作:在输入文件或文件夹之前或之后显示它们的名称。目前为止就这样了。再见!:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION