JavaRush /Java Blog /Random-TW /Java 檔案、路徑

Java 檔案、路徑

在 Random-TW 群組發布
你好!今天我們將討論如何使用文件和目錄。您已經知道如何管理文件的內容:我們有很多專門用於此目的的類別:)我認為您可以輕鬆記住實現這些目的所需的幾個類別。在今天的講座中,我們將具體討論文件管理——創建、重新命名等。在 Java 7 之前,所有此類操作都是使用File. 您可以在這裡閱讀有關他的作品。但在 Java 7 中,該語言的創建者決定改變他們處理檔案和目錄的方式。這是因為課程File有許多缺點。例如,它沒有一種方法copy()可以讓您將文件從一個位置複製到另一個位置(看似顯然需要的功能)。此外,該類別File還有很多傳回boolean值的方法。如果發生錯誤,此類方法將傳回false而不是拋出異常,這使得診斷錯誤並確定其原因變得非常困難。出現的不是單一類,而是File多達 3 個類:PathsPathFiles。嗯,準確地說,Path這是一個接口,而不是一個類別。讓我們弄清楚它們之間有何不同以及為什麼需要它們。讓我們從最簡單的事情開始— Paths

路徑

Paths是一個非常簡單的類,只有一個靜態方法get()。它的創建只是為了從傳遞的 string 或 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方法傳回了這個String、那個boolean、那個File——這並不容易弄清楚。例如,有一個方法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\使用者名稱\桌面
    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"));
       }
    }

    控制台輸出:


    您需要將完整路徑傳遞給方法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());
       }
    }

    控制台輸出:

    真的

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

    控制台輸出:

    使用者名稱\桌面\testFile.txt

完整的方法列表Path相當大。您可以在Oracle 文件中找到它們。我們將繼續進行審查Files

文件

Files- 這是一個實用程式類,其中移動了類別中的靜態方法File。- 這與orFiles大致相同,只是它適用於文件,而不適用於數組和集合:) 它專注於管理文件和目錄。使用靜態方法,我們可以建立、刪除和移動檔案和目錄。對於這些操作,使用方法(對於目錄 - )和。以下是如何使用它們: ArraysCollectionsFilescreateFile()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 {

       //file creation
       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 directory
       Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));

       //move file from desktop to testDirectory. You need to move with the name of the file in the folder!
       testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Is our file left 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")));

       //remove 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())。之後,我們將檔案(method Files.move())從桌面移到這個新資料夾,最後我們刪除該檔案(method Files.delete())。 控制台輸出: 檔案建立成功嗎?true 目錄建立成功嗎?true 我們的檔案還在桌面上嗎?false 我們的檔案是否已移至 testDirectory?true 檔案是否仍然存在?錯誤的 注意:就像介面方法一樣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 {

       //file creation
       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 directory
       Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));

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

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

       System.out.println("Has our file been copied to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
   }
}
控制台輸出: 檔案建立成功嗎?true 目錄建立成功嗎?true 我們的檔案還在桌面上嗎?true 我們的檔案是否已複製到 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);
       }
   }
}
控制台輸出: 我記得一個美妙的時刻:你出現在我面前,像轉瞬即逝的幻象,像一個純粹美麗的天才。 很舒服!:)這個特性在Java 7中出現。在Java 8中,出現了Stream API,它為Java添加了一些函數式程式設計的元素。包括更豐富的文件處理功能。想像一下,我們有一個任務:找到文件中以“How”一詞開頭的所有行,將它們轉換為大寫並將它們輸出到控制台。在 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("How")) {
               String upper = s.toUpperCase();
               result.add(upper);
           }
       }

       for (String s: result) {
           System.out.println(s);
       }
   }
}
控制台輸出: LIKE A FASTING VISION, LIKE A GENIUS OF PURE BEAUTY。 我們似乎已經做到了,但是您不認為對於這樣一個簡單的任務,我們的程式碼有點……冗長嗎?使用 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("How"))
               .map(String::toUpperCase)
               .collect(Collectors.toList());
       result.forEach(System.out::println);
   }
}
我們取得了相同的結果,但程式碼少得多!而且,也不能說我們輸了「可讀性」。我認為即使您不熟悉 Stream API,您也可以輕鬆評論這段程式碼的作用。但簡而言之,Stream是一個元素序列,您可以在其中執行不同的功能。我們從方法中取得 Stream 對象Files.lines(),然後對其應用 3 個函數:
  1. 使用該方法,filter()我們僅從檔案中選擇以“How”開頭的那些行。

  2. 我們使用該方法遍歷所有選定的行map(),並將每一行減少為大寫。

  3. 我們將所有產生的行組合起來List使用collect().

在出口處,我們得到了相同的結果: 就像一個快速的願景,就像一個純粹美麗的天才。 如果您有興趣了解有關該庫的功能的更多信息,我們建議您閱讀這篇文章。我們將回到我們的羊,即文件:)我們今天要考慮的最後一種可能性是遍歷文件樹。現代作業系統中的檔案結構通常採用樹的形式:它有一個根,還有可以與其他分支分開的分支,等等。目錄起著根和分支的作用。例如,目錄“ C:// ”可以充當根目錄。從它分支出兩個分支:「C://Downloads」和「C://Users」。每個分支還有 2 個分支:「C://Downloads/Pictures」、「C://Downloads/Video」、「C://Users/JohnSmith」、「C://Users/Pudge2005」 。其他分支從這些分支中分支出來,等等。- 這就是一棵樹的樣子。在 Linux 中看起來差不多,只是目錄充當根目錄/ 文件、路徑 - 2現在想像我們有一個任務:知道根目錄,我們必須遍歷它,查看所有級別的資料夾並找到其中包含內容的檔案我們需要。我們將尋找包含“這是我們需要的文件!”這一行的文件。我們的根目錄將是位於桌面上的「testFolder」資料夾。裡面有以下內容: 文件、路徑 - 3在資料夾 level1-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("Required file found!");
               System.out.println(file.toAbsolutePath());
               break;
           }
       }

       return FileVisitResult.CONTINUE;
   }
}
在這種情況下,我們的類別繼承自SimpleFileVisitor. 這是一個實作 的類FileVisitor,您只需重寫其中一個方法:visitFile()。這裡我們描述了每個目錄中的每個檔案需要做什麼。如果您需要更複雜的遍歷邏輯,您應該編寫自己的實作FileVisitor。在那裡你需要實作另外 3 個方法:
  • preVisitDirectory()— 進入資料夾前必須執行的邏輯;

  • visitFileFailed()— 如果無法輸入檔案(無法存取或其他原因)該怎麼辦;

  • postVisitDirectory()——進入資料夾後必須執行的邏輯。

我們沒有這樣的邏輯,所以對我們來說就夠了SimpleFileVisitor。此方法內部的邏輯visitFile()非常簡單:從檔案中讀取所有行,檢查它們是否包含我們需要的內容,如果包含,則將絕對路徑列印到控制台。唯一可能給你帶來麻煩的一行是這一行:
return FileVisitResult.CONTINUE;
其實,一切都很簡單。這裡我們簡單描述一下檔案輸入並完成所有必要的操作後程式應該做什麼。在我們的例子中,我們需要繼續遍歷樹,所以我們選擇選項CONTINUE。但是,例如,我們可能有另一個任務:不是找到所有包含「這是我們需要的文件」的文件,而是只找到一個這樣的文件。此後,必須終止該程序。在這種情況下,我們的程式碼看起來完全相同,但不是中斷;會:
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());
   }
}
控制台輸出: 已找到所需文件!C:\Users\使用者名稱\Desktop\testFolder\FileWeNeed1.txt 找到所需檔案!C:\Users\使用者名稱\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt 找到所需檔案!C:\Users\使用者名稱\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt 太好了,我們做到了!:) 如果你想了解更多walkFileTree(),我推薦你這篇文章。您還可以完成一項小任務 - 將其替換SimpleFileVisitor為常規任務FileVisitor,實現所有 4 種方法並為該程式提出一個目的。例如,您可以編寫一個程式來記錄您的所有操作:在輸入檔案或資料夾之前/之後在控制台中顯示檔案或資料夾的名稱。就這樣 - 稍後見!:)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION