JavaRush /Blog Java /Random-MS /Fail Java, Laluan

Fail Java, Laluan

Diterbitkan dalam kumpulan
hello! Hari ini kita akan bercakap tentang bekerja dengan fail dan direktori. Anda sudah tahu cara mengurus kandungan fail: kami mempunyai banyak kelas khusus untuk ini :) Saya rasa anda boleh mengingati beberapa kelas yang diperlukan untuk tujuan ini dengan mudah. Dalam kuliah hari ini kita akan bercakap secara khusus tentang pengurusan fail - mencipta, menamakan semula, dll. Sebelum Java 7, semua operasi sedemikian dilakukan menggunakan File. Anda boleh membaca tentang karya beliau di sini . Tetapi dalam Java 7, pencipta bahasa memutuskan untuk menukar cara mereka berfungsi dengan fail dan direktori. Ini disebabkan oleh fakta bahawa kelas itu Filemempunyai beberapa kelemahan. Sebagai contoh, ia tidak mempunyai kaedah copy()yang membolehkan anda menyalin fail dari satu lokasi ke lokasi lain (ciri yang kelihatan sangat diperlukan). Di samping itu, kelas Filemempunyai banyak kaedah yang mengembalikan boolean-nilai. Jika ralat berlaku, kaedah sedemikian mengembalikan palsu dan bukannya membuang pengecualian, yang menyukarkan mendiagnosis ralat dan menentukan puncanya. Daripada satu kelas, Filesebanyak 3 kelas muncul: Paths, Pathdan Files. Nah, lebih tepatnya, Pathini adalah antara muka, bukan kelas. Mari kita fikirkan bagaimana mereka berbeza antara satu sama lain dan mengapa setiap daripada mereka diperlukan. Mari kita mulakan dengan perkara yang paling mudah - Paths.

Laluan

Pathsialah kelas yang sangat mudah dengan kaedah statik tunggal get(). Ia dicipta semata-mata untuk mendapatkan objek jenis daripada rentetan atau URI yang diluluskan Path. Ia tidak mempunyai fungsi lain. Berikut adalah contoh karya beliau:
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");
   }
}
Bukan kelas yang paling sukar, bukan? :) Nah, kerana kita mendapat objek jenis Path, mari kita fikirkan apa itu Pathdan mengapa ia diperlukan :)

Laluan

Path, pada umumnya, ialah analog yang direka bentuk semula bagi File. Ia lebih mudah untuk bekerja daripada dengan File. Pertama , banyak kaedah utiliti (statik) telah dialih keluar daripadanya dan dipindahkan ke kelas Files. Kedua , Pathnilai pulangan kaedah telah dipesan. Di dalam kelas, Filekaedah mengembalikan this String, that boolean, that File- tidak mudah untuk difikirkan. Sebagai contoh, terdapat kaedah getParent()yang mengembalikan laluan induk untuk fail semasa sebagai rentetan. Tetapi pada masa yang sama terdapat kaedah getParentFile()yang mengembalikan perkara yang sama, tetapi dalam bentuk objek File! Ini jelas berlebihan. Oleh itu, dalam antara muka, Pathkaedah getParent()dan kaedah lain untuk bekerja dengan fail hanya mengembalikan objek Path. Tiada banyak pilihan - semuanya mudah dan ringkas. Apakah kaedah berguna yang ada padanya Path? Berikut adalah beberapa daripada mereka dan contoh kerja mereka:
  • getFileName()— mengembalikan nama fail daripada laluan;

  • getParent()— mengembalikan direktori "ibu bapa" berhubung dengan laluan semasa (iaitu, direktori yang lebih tinggi dalam pepohon direktori);

  • getRoot()— mengembalikan direktori "root"; iaitu, yang berada di bahagian atas pokok direktori;

  • startsWith(), endsWith()— semak sama ada laluan bermula/berakhir dengan laluan yang diluluskan:

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

    Output konsol:

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

    Beri perhatian kepada cara kaedah itu berfungsi endsWith(). Ia menyemak sama ada laluan semasa berakhir dengan laluan yang diluluskan . Ia berada di laluan , dan bukan pada set watak .

    Bandingkan keputusan kedua-dua panggilan ini:

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

    Output konsol:

    salah,
    benar

    Anda perlu menghantar laluan penuh kepada kaedah endsWith(), dan bukan hanya satu set aksara: jika tidak, hasilnya akan sentiasa palsu , walaupun laluan semasa sebenarnya berakhir dengan jujukan aksara sedemikian (seperti dalam kes “estFile.txt ” dalam contoh di atas).

    Di samping itu, terdapat Pathsekumpulan kaedah yang memudahkan kerja dengan laluan mutlak (penuh) dan relatif .

Mari lihat kaedah ini:
  • boolean isAbsolute()— kembali benar jika laluan semasa adalah mutlak:

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

    Output konsol:

    benar

  • Path normalize()— "menormalkan" laluan semasa, mengalih keluar elemen yang tidak perlu daripadanya. Anda mungkin tahu bahawa sistem pengendalian popular sering menggunakan aksara “.” apabila menandakan laluan. (“direktori semasa”) dan “..” (direktori induk). Contohnya: “ ./Pictures/dog.jpg ” bermaksud bahawa dalam direktori yang kita ada sekarang, terdapat folder Pictures, dan di dalamnya terdapat fail “dog.jpg”

    Jadi begini. Jika program anda mempunyai laluan yang menggunakan "." atau "..", kaedah normalize()akan mengalih keluarnya dan mendapat laluan yang tidak mengandunginya:

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

    Output konsol:

    C:\Users\Java\examples
    C:\Users\examples

  • Path relativize()— mengira laluan relatif antara laluan semasa dan laluan yang dilalui.

    Sebagai contoh:

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

    Output konsol:

    Nama pengguna\Desktop\testFile.txt

Senarai penuh kaedah Pathagak besar. Anda boleh menemui kesemuanya dalam dokumentasi Oracle . Kami akan meneruskan untuk mengkaji semula Files.

Fail

Files- ini ialah kelas utiliti di mana kaedah statik dari kelas telah dialihkan File. Files- ini lebih kurang sama dengan Arraysatau Collections, hanya ia berfungsi dengan fail, dan bukan dengan tatasusunan dan koleksi :) Ia tertumpu pada mengurus fail dan direktori. Menggunakan kaedah statik Files, kami boleh mencipta, memadam dan memindahkan fail dan direktori. Untuk operasi ini kaedah digunakan createFile()(untuk direktori - createDirectory()), move()dan delete(). Begini cara menggunakannya:
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")));
   }
}
Di sini kita mula-mula membuat fail (kaedah Files.createFile()) pada desktop, kemudian buat folder di sana (method Files.createDirectory()). Selepas itu, kami memindahkan fail (kaedah Files.move()) dari desktop ke folder baharu ini, dan pada akhirnya kami memadamkan fail (kaedah Files.delete()). Output konsol: Adakah fail berjaya dibuat? benar Adakah direktori itu berjaya dibuat? benar Adakah fail kita masih pada desktop? false Adakah fail kami telah dialihkan ke testDirectory? benar Adakah fail masih wujud? salah Beri perhatian:Sama seperti kaedah antara muka Path, banyak kaedah Filesmengembalikan objekPath . Kebanyakan kaedah kelas Filesjuga menerima Path. Di sini kaedah akan menjadi pembantu setia anda Paths.get()- gunakannya secara aktif. Apa lagi yang menarik Files? Apa yang sebenarnya kekurangan kelas lama ialah kaedah ! File. copy()Kami bercakap tentang dia pada awal kuliah, sekarang adalah masa untuk bertemu dengannya!
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")));
   }
}
Output konsol: Adakah fail berjaya dibuat? benar Adakah direktori itu berjaya dibuat? benar Adakah fail kita masih pada desktop? true Adakah fail kami disalin ke testDirectory? benar Kini anda boleh menyalin fail secara pemrograman! :) Tetapi kelas Filesmembolehkan anda bukan sahaja menguruskan fail itu sendiri, tetapi juga untuk bekerja dengan kandungannya. Untuk menulis data ke fail, ia mempunyai kaedah write(), dan untuk membaca - sebanyak 3:, read()dan readAllBytes()Kami readAllLines() akan membincangkan yang terakhir secara terperinci. Mengapa di atasnya? Kerana ia mempunyai jenis pulangan yang sangat menarik - List<String>! Iaitu, ia mengembalikan kita senarai baris dalam fail. Sudah tentu, ini menjadikan kerja dengan kandungan sangat mudah, kerana keseluruhan fail, baris demi baris, boleh, sebagai contoh, menjadi output ke konsol dalam gelung biasa 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);
       }
   }
}
Output konsol: Saya ingat detik indah: Anda muncul di hadapan saya, Seperti penglihatan sekejap, Seperti genius kecantikan murni. Sangat selesa! :) Ciri ini muncul dalam Java 7. Dalam Java 8, Stream API muncul , yang menambahkan beberapa elemen pengaturcaraan berfungsi pada Java. Termasuk keupayaan yang lebih kaya untuk bekerja dengan fail. Bayangkan kita mempunyai tugas: cari semua baris dalam fail yang bermula dengan perkataan "Bagaimana", tukarkannya kepada HURUF ATAS dan keluarkannya ke konsol. Apakah rupa penyelesaian menggunakan kelas Filesdalam Java 7? Sesuatu seperti ini:
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);
       }
   }
}
Output konsol: SEPERTI PENGLIHATAN PUASA, SEPERTI GENIUS KECANTIKAN MURNI. Kami nampaknya telah melakukannya, tetapi tidakkah anda fikir bahawa untuk tugasan yang begitu mudah, kod kami ternyata sedikit... verbose? Menggunakan Java 8 Stream API penyelesaiannya kelihatan lebih elegan:
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);
   }
}
Kami mencapai hasil yang sama, tetapi dengan lebih sedikit kod! Lebih-lebih lagi, tidak boleh dikatakan bahawa kita telah kalah dalam "kebolehbacaan". Saya fikir anda boleh mengulas dengan mudah tentang apa yang dilakukan oleh kod ini, walaupun anda tidak biasa dengan API Strim. Tetapi secara ringkasnya, Strim ialah jujukan elemen di mana anda boleh melaksanakan fungsi yang berbeza. Kami mendapat objek Stream daripada kaedah Files.lines()dan kemudian menggunakan 3 fungsi padanya:
  1. Menggunakan kaedah ini, filter()kami hanya memilih baris tersebut daripada fail yang bermula dengan "Bagaimana".

  2. Kami melalui semua baris yang dipilih menggunakan kaedah map()dan mengurangkan setiap baris kepada HURUF ATAS.

  3. Kami menggabungkan semua baris yang terhasil untuk Listmenggunakan collect().

Di pintu keluar kami mendapat hasil yang sama: SEPERTI PENGLIHATAN PUASA, SEPERTI GENIUS KECANTIKAN MURNI. Jika anda berminat untuk mengetahui lebih lanjut tentang keupayaan perpustakaan ini, kami mengesyorkan membaca artikel ini . Kami akan kembali kepada domba kami, iaitu, fail :) Kemungkinan terakhir yang akan kami pertimbangkan hari ini ialah berjalan melalui pokok fail . Struktur fail dalam sistem pengendalian moden paling kerap mengambil bentuk pokok: ia mempunyai akar dan terdapat cawangan yang mana cawangan lain boleh dipisahkan, dsb. Direktori memainkan peranan sebagai akar dan cabang. Sebagai contoh, direktori " C:// " boleh bertindak sebagai akar . Dua cabang bercabang daripadanya: “ C://Downloads ” dan “ C://Users ”. Daripada setiap cawangan ini terdapat 2 lagi cawangan: “ C://Muat Turun/Gambar ”, “ C://Muat Turun/Video ”, “ C://Users/JohnSmith ”, “ C://Users/Pudge2005 ” . Cawangan lain bercabang dari cawangan ini, dsb. - Beginilah rupa pokok. Di Linux ia kelihatan hampir sama, hanya di sana direktori bertindak sebagai akar / Fail, Laluan - 2 Sekarang bayangkan bahawa kita mempunyai tugas: mengetahui direktori akar, kita mesti melaluinya, melihat ke dalam folder dari semua peringkat dan mencari fail di dalamnya dengan kandungan kita perlu. Kami akan mencari fail yang mengandungi baris "Ini adalah fail yang kami perlukan!" Direktori akar kami akan menjadi folder "testFolder", yang terletak pada desktop. Di dalamnya terdapat kandungan berikut: Fail, Laluan - 3Di dalam folder level1-a dan level1-b terdapat juga folder: Fail, Laluan - 4Fail, Laluan - 5Di dalam "folder peringkat kedua" ini tidak ada lagi folder, hanya fail individu: Fail, Laluan - 6Fail, Laluan - 7Kami akan menetapkan 3 fail khas dengan kandungan yang kami perlukan dengan nama yang jelas - FileWeNeed1.txt , FileWeNeed2.txt, FileWeNeed3.txt Ini adalah yang perlu kita cari melalui kandungan menggunakan Java. Bagaimana kita boleh melakukan ini? Kaedah yang sangat berkuasa untuk melintasi pokok fail datang untuk menyelamatkan - Files.walkFileTree(). Inilah yang perlu kita lakukan. Pertama, kita perlukan FileVisitor. FileVisitorialah antara muka khas yang menerangkan semua kaedah untuk melintasi pepohon fail. Khususnya, kami akan meletakkan logik di sana untuk membaca kandungan fail dan menyemak sama ada ia mengandungi teks yang kami perlukan. Inilah rupa kita 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;
   }
}
Dalam kes ini, kelas kami mewarisi daripada SimpleFileVisitor. Ini ialah kelas yang melaksanakan FileVisitor, di mana anda perlu mengatasi hanya satu kaedah: visitFile(). Di sini kami menerangkan perkara yang perlu dilakukan dengan setiap fail dalam setiap direktori. Jika anda memerlukan logik traversal yang lebih kompleks, anda harus menulis pelaksanaan anda sendiri FileVisitor. Di sana anda perlu melaksanakan 3 lagi kaedah:
  • preVisitDirectory()— logik yang mesti dilaksanakan sebelum memasuki folder;

  • visitFileFailed()— apa yang perlu dilakukan jika memasukkan fail adalah mustahil (tiada akses, atau sebab lain);

  • postVisitDirectory()— logik yang mesti dilaksanakan selepas memasuki folder.

Kami tidak mempunyai logik sedemikian, jadi ia cukup untuk kami SimpleFileVisitor. Logik di dalam kaedah ini visitFile()agak mudah: baca semua baris dari fail, semak sama ada ia mengandungi kandungan yang kami perlukan, dan jika ya, cetak laluan mutlak ke konsol. Satu-satunya baris yang mungkin memberi anda masalah ialah yang ini:
return FileVisitResult.CONTINUE;
Sebenarnya, semuanya mudah. Di sini kami hanya menerangkan perkara yang perlu dilakukan oleh program selepas fail dimasukkan dan semua operasi yang diperlukan telah selesai. Dalam kes kami, kami perlu terus melintasi pokok itu, jadi kami memilih pilihan CONTINUE. Tetapi kami, sebagai contoh, mungkin mempunyai tugas lain: untuk mencari bukan semua fail yang mengandungi "Ini adalah fail yang kami perlukan", tetapi hanya satu fail sedemikian . Selepas ini, program mesti ditamatkan. Dalam kes ini, kod kami akan kelihatan sama, tetapi bukannya break; akan:
return FileVisitResult.TERMINATE;
Baiklah, mari jalankan kod kami dan lihat sama ada ia berfungsi.
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());
   }
}
Output konsol: Fail yang diperlukan telah ditemui! C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt Fail yang diperlukan telah ditemui! C:\Users\Username\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt Fail yang diperlukan telah ditemui! C:\Users\Username\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt Hebat, kami berjaya! :) Jika anda ingin mengetahui lebih lanjut tentang walkFileTree(), saya syorkan anda artikel ini . Anda juga boleh menyelesaikan tugasan kecil - gantikannya SimpleFileVisitordengan tugas biasa FileVisitor, laksanakan kesemua 4 kaedah dan buat tujuan untuk program ini. Sebagai contoh, anda boleh menulis program yang akan log semua tindakan anda: paparkan nama fail atau folder dalam konsol sebelum/selepas memasukkannya. Itu sahaja - jumpa lagi! :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION