CodeGym /Java blog /Véletlen /Java fájlok, elérési út
John Squirrels
Szint
San Francisco

Java fájlok, elérési út

Megjelent a csoportban
Szia! Ma a fájlokkal és könyvtárakkal való munkáról fogunk beszélni. Te már tudod, hogyan kell kezelni a fájltartalmakat: rengeteg leckét szenteltünk ennek :) Szerintem könnyen megjegyezhetsz néhány ilyen célra használt osztályt. A mai leckében kifejezetten a fájlkezelésről lesz szó: létrehozásról, átnevezésről stb. A Java 7 előtt minden ilyen műveletet a Fájl osztály segítségével hajtottak végre. Itt olvashatsz róla . A Java 7-ben azonban a nyelv készítői úgy döntöttek, hogy megváltoztatják a fájlokkal és könyvtárakkal való munkamódszerünket. Ez azért történt, mert a File osztálynak számos hátránya volt. Például nem volt benne a copy() metódus, amely lehetővé tenné, hogy egy fájlt egyik helyről a másikra másoljon (egy alapvetőnek tűnő képesség). Ezen kívül aA fájlosztálynak jó néhány olyan metódusa volt, amely logikai értékeket adott vissza. Hiba esetén az ilyen metódus false értéket ad vissza. Nem jelent kivételt, nagyon megnehezíti a hibák azonosítását és okainak diagnosztizálását. Az egyetlen File osztály helyén 3 osztály jelent meg: Paths , Path és Files . Nos, hogy pontos legyek, a Path egy interfész, nem egy osztály. Nézzük meg, miben különböznek egymástól, és miért van szükségünk mindegyikre. Kezdjük a legegyszerűbbel: Útvonalak .

Ösvények

A Paths egy nagyon egyszerű osztály egyetlen statikus metódussal: get() . Kizárólag azért jött létre, hogy az átadott karakterláncból vagy URI-ból Path objektumot kapjon. Nincs más funkciója. Íme egy példa a munkahelyen:

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");
   }
}
Nem a legösszetettebb osztály, igaz? :) Nos, nálunk is megvan ez a Path típus. Találjuk ki, mi az a Path és miért van rá szükség :)

Pálya

A Path nagyjából a File osztály újratervezett analógja. Sokkal könnyebb vele dolgozni, mint a File -al . Először sok segédprogram (statikus) metódust vettek ki és helyeztek át a Fájlok osztályba. Másodszor , a Path interfész metódusainak visszatérési értékei sorrendbe kerültek . A Fájl osztályban a metódusok karakterláncot , logikai értéket vagy fájlt adtak vissza . Nem volt könnyű rájönni. Például volt egy getParent() metódus, amely az aktuális fájl szülőútvonalát képviselő karakterláncot adott vissza. De volt még agetParentFile() metódus, amely ugyanazt adta vissza, de File objektum formájában! Ez egyértelműen felesleges. Ennek megfelelően a Path felületen a getParent() metódus és a fájlokkal való munkavégzés egyéb metódusai egyszerűen egy Path objektumot adnak vissza. Nincs halom lehetőség – minden könnyű és egyszerű. Melyek a Path hasznos módszerei ? Íme néhány közülük, és példák a működésükre:
  • getFileName() : visszaadja a fájlnevet az elérési útból;

  • getParent() : az aktuális elérési út "szülő" könyvtárát adja vissza (más szóval a könyvtárfában közvetlenül fent található könyvtárat);

  • getRoot() : a "root" könyvtárat adja vissza, azaz a könyvtárfa tetején lévő könyvtárat;

  • startsWith() , endsWith() : ellenőrizze, hogy az elérési út az átadott útvonallal kezdődik-e/végeződik-e:

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

    Konzol kimenet:

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

    Ügyeljen az endsWith() metódus működésére. Ellenőrzi, hogy az aktuális útvonal véget ér-e az átadott útvonallal . Pontosabban, hogy az elérési útban van-e , és nem az átadott karakterláncban .

    Hasonlítsa össze a két hívás eredményét:

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

    Konzol kimenet:

    
    false
    true
    

    Az endsWith() metódusnak valódi elérési utat kell átadni, nem csak karakterkészletet: ellenkező esetben az eredmény mindig hamis lesz, még akkor is, ha az aktuális elérési út valóban ezzel a karaktersorozattal végződik (mint az "estFile.txt" esetében " a fenti példában).

    Ezen túlmenően, a Path rendelkezik egy olyan módszercsoporttal, amely leegyszerűsíti az abszolút (teljes) és relatív útvonalakkal való munkát .

Nézzük ezeket a módszereket:
  • A logikai isAbsolute() igazat ad vissza, ha az aktuális elérési út abszolút:

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

    Konzol kimenet:

    
    true
    
  • Path normalize() : "normalizálja" az aktuális útvonalat, eltávolítja belőle a felesleges elemeket. Talán tudja, hogy a népszerű operációs rendszerekben a "." (aktuális könyvtár) és ".." (szülőkönyvtár) gyakran használják az elérési utak kijelölésére. Például a " ./Pictures/dog.jpg " azt jelenti, hogy az aktuális könyvtárban van egy "Pictures" mappa, amely viszont egy "kutya.jpg" fájlt tartalmaz.

    Nézz ide. Ha egy elérési út a "." vagy ".." jelenik meg a programban, a normalize() metódus eltávolítja őket, és létrehoz egy olyan elérési utat, amely nem tartalmazza őket:

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

    Konzol kimenet:

    
    C:\Users\Java\examples
    C:\Users\examples
    
  • Path relativize() : kiszámítja a relatív utat az aktuális és az átadott út között.

    Például:

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

    Konzol kimenet:

    
    Username\Desktop\testFile.txt
    

A Path metódusok teljes listája meglehetősen hosszú. Mindegyiket megtalálja az Oracle dokumentációjában . Most áttérünk a Fájlokra .

Fájlok

A Files egy segédprogramosztály, amely a Fájl osztályból kivett statikus metódusokat tartalmazza. A Files hasonló a tömbökhöz vagy a gyűjteményekhez . A különbség az, hogy fájlokkal működik, nem tömbökkel vagy gyűjteményekkel :) A fájlok és könyvtárak kezelésére koncentrál. A Fájlok osztály statikus metódusai segítségével fájlokat és könyvtárakat hozhatunk létre, törölhetünk, mozgathatunk. Ezeket a műveleteket a createFile() (könyvtárak esetén createDirectory() ), move() és delete() metódusokkalhajtjuk végreA következőképpen használhatja őket:

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")));
   }
}
Itt először létrehozunk egy fájlt ( Files.createFile() metódus) az asztalon. Ezután létrehozunk egy mappát ugyanazon a helyen ( Files.createDirectory() metódus). Ezt követően áthelyezzük a fájlt ( Files.move() metódus) az asztalról ebbe az új mappába, végül töröljük a fájlt ( Files.delete() metódus). Konzol kimenet:

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
Jegyzet:Pathaz interfész metódusaihoz hasonlóan az osztály számos metódusa is objektumot Filesad visszaPath . Az osztály legtöbb metódusa objektumokat Filesis Pathbemenetként vesz fel. Itt a Paths.get()módszer az Ön hűséges asszisztense lesz – használja ki jól. Mi az érdekes még Files? Ami a régi Fileosztályból nagyon hiányzott, az egy copy()módszer! A lecke elején beszéltünk róla. Itt az ideje, hogy találkozzunk vele!

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

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
Most már tudja, hogyan másolhat fájlokat programozottan! :) Természetesen az Filesosztályban nem csak magát a fájlt kezelhetjük, hanem a tartalmával is dolgozhatunk. Rendelkezik az write()adatok fájlba írásának módszerével, és mind a 3 módszerrel az adatok beolvasására: read(), readAllBytes(), és readAllLines() Az utolsónál részletesen kitérünk. Miért pont az? Mert nagyon érdekes visszatérési típusa van: List<String>! Azaz visszaadja nekünk a fájl összes sorának listáját. Ez természetesen nagyon kényelmessé teszi a fájltartalommal való munkát, mert a teljes fájl soronként megjeleníthető például a konzolon egy közönséges forciklus segítségével:

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);
       }
   }
}
Konzol kimenet:

I still recall the wondrous moment: 
When you appeared before my sight, 
As though a brief and fleeting omen, 
Pure phantom in enchanting light.
Szuper kényelmes! :) Ez a képesség a Java 7-ben jelent meg. A Stream API a Java 8-ban jelent meg. A funkcionális programozás néhány elemét hozzáadja a Java-hoz. Gazdagabb fájlkezelési képességekkel. Képzeljük el, hogy a következő feladatunk van: keressük meg az „As” szóval kezdődő összes sort, alakítsuk NAGYBETŐSRE, és jelenítsük meg a konzolon. Hogyan nézne ki az osztályt használó megoldás FilesJava 7-ben? Valami ilyesmi:

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);
       }
   }
}
Konzol kimenet:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
A küldetés teljesítve, de nem gondolja, hogy egy ilyen egyszerű feladathoz a kódunk kicsit... bőbeszédűnek bizonyult? A Java 8 Stream API-ját használva a megoldás sokkal elegánsabbnak tűnik:

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);
   }
}
Ugyanazt az eredményt értük el, de sokkal kevesebb kóddal! Ráadásul senki sem mondhatja, hogy elveszítettük az „olvashatóságot”. Azt hiszem, könnyen kommentálhatja, mit csinál ez a kód, még akkor is, ha nem ismeri a Stream API-t. Röviden, a Stream olyan elemek sorozata, amelyeken keresztül különféle műveleteket hajthat végre. A metódusból egy Stream objektumot kapunk Files.lines(), majd 3 függvényt alkalmazunk rá:
  1. A filter()módszerrel csak azokat a sorokat jelöljük ki a fájlból, amelyek "As"-vel kezdődnek.

  2. A metódussal végigjárjuk az összes kiválasztott sort map(), és mindegyiket NAGYBETŰRE alakítjuk.

  3. A módszert arra használjuk, collect()hogy az összes kapott sort egy List.

Ugyanazt a kimenetet kapjuk:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Most térjünk vissza a kenyerünkhöz, vagyis a fájlokhoz :) Az utolsó lehetőség, amit ma figyelembe veszünk, a reszelőfán való séta . A modern operációs rendszerekben a fájlstruktúra leggyakrabban fának tűnik: van gyökér, és vannak ágak, amelyeknek lehetnek más ágai stb. A gyökér és az ágak könyvtárak. Például a " С:// " könyvtár lehet a gyökér. Két ágat tartalmaz: " C://Downloads " és " C://Users ". Mindegyik ágnak két ága van: " C://Downloads/Pictures ", " C://Downloads/Video ", " C://Users/JohnSmith ", " C://Users/Pudge2005"". És ezeknek az ágaknak vannak más ágai stb. és ezért hívjuk fának. Linuxon a szerkezet hasonló, de a / könyvtár Fájlok, elérési út - 2a gyökér. Most képzeljük el, hogy a gyökérkönyvtárból kell kezdenünk , végigjárja az összes mappát és almappát, és megkeresi azokat a fájlokat, amelyek bizonyos tartalmat tartalmaznak. Megkeressük azokat a fájlokat, amelyek a következő sort tartalmazzák: „Ez az a fájl, amire szükségünk van!” Fogjuk a „testFolder” mappát, amely a desktop, mint gyökérkönyvtár. Íme a tartalma: Fájlok, elérési út - 3Az 1-a és 1-b szintű mappák is tartalmaznak mappákat: Fájlok, elérési út - 4Fájlok, elérési út - 5Ezekben a "második szintű mappákban" nincsenek mappák, csak egyedi fájlok: Fájlok, elérési út - 6Fájlok, elérési út - 7A szükséges tartalommal rendelkező 3 fájlnak szándékosan magyarázó nevet adtunk: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt. Pontosan ezeket a fájlokat kell megtalálnunk a Java használatával. Hogyan csináljuk ezt? Egy nagyon hatékony módszer a fájlfa bejárására jön a segítségünkre: Files.walkFileTree (). Íme, mit kell tennünk. Először is szükségünk van egy FileVisitor. FileVisitoregy speciális interfész, amelyen a fájlfa bejárásának módszerei vannak leírva. Konkrétan ide tesszük a fájl tartalmának beolvasásának logikáját és annak ellenőrzését, hogy az tartalmazza-e a szükséges szöveget. Nálunk így FileVisitornéz ki:

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;
   }
}
Ebben az esetben az osztályunk örökli SimpleFileVisitor. Ez egy olyan osztály, amely megvalósítja a -t FileVisitor, amelyben csak egy metódust kell felülbírálnunk: visitFile(). Itt meghatározzuk, hogy mit kell tenni az egyes könyvtárakban található fájlokkal. Ha bonyolultabb logikára van szüksége a fájlstruktúra bejárásához, akkor meg kell írnia a saját implementációját FileVisitor. Ebben az osztályban további 3 metódust kell megvalósítania:
  • preVisitDirectory(): a mappa belépés előtt végrehajtandó logika;

  • visitFileFailed(): a végrehajtandó logika, ha egy fájl nem látogatható (nincs hozzáférés, vagy egyéb okok miatt);

  • postVisitDirectory(): a mappa beírása után végrehajtandó logika.

Nincs szükségünk ilyen logikára, így rendben vagyunk SimpleFileVisitor. A visitFile()metóduson belüli logika meglehetősen egyszerű: olvassuk el a fájl összes sorát, ellenőrizzük, hogy tartalmazzák-e a számunkra szükséges tartalmat, és ha igen, nyomtassuk ki az abszolút elérési utat a konzolon. Az egyetlen sor, amely nehézséget okozhat, ez:

return FileVisitResult.CONTINUE;
Valójában ez nagyon egyszerű. Itt egyszerűen leírjuk, mit kell tennie a programnak a fájl meglátogatása és az összes szükséges művelet elvégzése után. Esetünkben szeretnénk folytatni a fán való bejárást, ezért a CONTINUElehetőséget választjuk. Másik megoldásként azonban más célunk is lehet: ahelyett, hogy megkeressük az összes olyan fájlt, amely tartalmazza az "Ez az a fájl, amire szükségünk van", csak egy ilyen fájlt keressen . Ezt követően a programnak le kell állnia. Ebben az esetben a kódunk pontosan ugyanúgy nézne ki, de a break helyett a következő lenne:

return FileVisitResult.TERMINATE;
Nos, futtassuk le a kódunkat, és nézzük meg, működik-e.

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

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
Kiváló! Működött! :) Elfogadhatod ezt a kis kihívást is: cseréld le SimpleFileVisitoregy közönséges -re FileVisitor, írd felül mind a 4 módszert, és találd ki a saját célodat a programhoz. Például írhat egy programot, amely minden műveletét naplózza: megjeleníti a fájl vagy mappa nevét a beírás előtt vagy után. Ez minden most. Hamarosan találkozunk! :)
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION