CodeGym /Java Blog /Willekeurig /Java-bestanden, pad
John Squirrels
Niveau 41
San Francisco

Java-bestanden, pad

Gepubliceerd in de groep Willekeurig
Hoi! Vandaag zullen we het hebben over het werken met bestanden en mappen. Je weet al hoe je bestandsinhoud moet beheren: we hebben hier veel lessen aan gewijd :) Ik denk dat je het gemakkelijk vindt om een ​​paar klassen te onthouden die voor deze doeleinden worden gebruikt. In de les van vandaag zullen we het specifiek hebben over bestandsbeheer: maken, hernoemen, enz. Vóór Java 7 werden al dergelijke bewerkingen uitgevoerd met behulp van de klasse File . U kunt er hier over lezen . Maar in Java 7 besloten de makers van de taal om de manier waarop we met bestanden en mappen werken te veranderen. Dit gebeurde omdat de klasse File verschillende nadelen had. Het beschikte bijvoorbeeld niet over de methode copy() waarmee u een bestand van de ene locatie naar de andere kunt kopiëren (een ogenschijnlijk essentiële vaardigheid). tevens deDe bestandsklasse had nogal wat methoden die booleaanse waarden retourneerden. Als er een fout is, retourneert zo'n methode false. Er wordt geen uitzondering gemaakt, waardoor het erg moeilijk wordt om fouten te identificeren en de oorzaken ervan vast te stellen. In plaats van de enkele File- klasse verschenen er 3 klassen: Paths , Path en Files . Om precies te zijn, Path is een interface, geen klasse. Laten we eens kijken hoe ze van elkaar verschillen en waarom we ze allemaal nodig hebben. Laten we beginnen met de eenvoudigste: Paden .

Paden

Paths is een zeer eenvoudige klasse met een enkele statische methode: get() . Het is uitsluitend gemaakt om een ​​Path- object op te halen uit de doorgegeven tekenreeks of URI. Het heeft geen andere functionaliteit. Hier is een voorbeeld van op het werk:

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");
   }
}
Niet de meest complexe klasse, toch? :) Nou, we hebben ook dit padtype . Laten we uitzoeken wat Path is en waarom het nodig is :)

Pad

Path is over het algemeen een opnieuw ontworpen analoog van de klasse File . Het is veel gemakkelijker om mee te werken dan Bestand . Eerst werden veel (statische) hulpprogramma-methoden verwijderd en verplaatst naar de klasse Files . Ten tweede werd er orde opgelegd aan de retourwaarden van de methoden van de Path- interface. In de klasse File retourneerden methoden een String , of een boolean , of een File . Het was niet gemakkelijk om erachter te komen. Er was bijvoorbeeld een methode getParent() die een tekenreeks retourneerde die het bovenliggende pad van het huidige bestand vertegenwoordigde. Maar er was ook eengetParentFile() methode, die hetzelfde retourneerde maar in de vorm van een File- object! Dit is duidelijk overbodig. Dienovereenkomstig retourneren in de Path- interface de methode getParent() en andere methoden voor het werken met bestanden gewoon een Path- object. Geen stapel opties - alles is gemakkelijk en eenvoudig. Wat zijn enkele van de handige methoden die Path heeft? Hier zijn enkele van hen en voorbeelden van hoe ze werken:
  • getFileName() : retourneert de bestandsnaam van het pad;

  • getParent() : retourneert de "bovenliggende" map van het huidige pad (met andere woorden, de map direct boven in de mappenstructuur);

  • getRoot() : geeft de "root"-directory terug, dwz de directory bovenaan de directory-boom;

  • startsWith() , endsWith() : controleer of het pad begint/eindigt met het doorgegeven pad:

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

    Console-uitvoer:

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

    Let op hoe de methode endsWith() werkt. Het controleert of het huidige pad eindigt met het doorgegeven pad . Specifiek, of het nu in het pad staat , niet in de doorgegeven string .

    Vergelijk de resultaten van deze twee oproepen:

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

    Console-uitvoer:

    
    false
    true
    

    Aan de methode endsWith() moet een echt pad worden doorgegeven, niet alleen een reeks tekens: anders is het resultaat altijd onwaar, zelfs als het huidige pad echt eindigt met die reeks tekens (zoals het geval is met "estFile.txt " in het bovenstaande voorbeeld).

    Daarnaast heeft Pad een groep methoden die het werken met absolute (volledige) en relatieve paden vereenvoudigt .

Laten we naar deze methoden kijken:
  • boolean isAbsolute() retourneert true als het huidige pad absoluut is:

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

    Console-uitvoer:

    
    true
    
  • Path normalize() : "normaliseert" het huidige pad, waarbij onnodige elementen eruit worden verwijderd. U weet misschien dat in populaire besturingssystemen de symbolen "." (huidige map) en ".." (bovenliggende map) worden vaak gebruikt om paden aan te duiden. " ./Pictures/dog.jpg " betekent bijvoorbeeld dat de huidige map een map "Pictures" heeft, die op zijn beurt een bestand "dog.jpg" bevat.

    Kijk hier. Als een pad met "." of ".." in uw programma verschijnt, zal de methode normalize() ze verwijderen en een pad produceren dat ze niet bevat:

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

    Console-uitvoer:

    
    C:\Users\Java\examples
    C:\Users\examples
    
  • Path relativize() : berekent het relatieve pad tussen het huidige en het doorgegeven pad.

    Bijvoorbeeld:

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

    Console-uitvoer:

    
    Username\Desktop\testFile.txt
    

De volledige lijst met Path- methoden is vrij lang. U vindt ze allemaal in de Oracle-documentatie . Nu gaan we verder met het overwegen van Bestanden .

Bestanden

Files is een hulpprogrammaklasse die de statische methoden bevat die uit de File- klasse zijn gehaald. Bestanden is vergelijkbaar met Arrays of Collections . Het verschil is dat het werkt met bestanden, niet met arrays of verzamelingen :) Het richt zich op het beheren van bestanden en mappen. Met behulp van de statische methoden van de klasse Files kunnen we bestanden en mappen maken, verwijderen en verplaatsen. Deze bewerkingen worden uitgevoerd met behulp van demethoden createFile() (voor mappen, createDirectory() ), move() en delete() . Zo gebruikt u ze:

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")));
   }
}
Hier maken we eerst een bestand ( methode Files.createFile() ) op het bureaublad. Vervolgens maken we een map op dezelfde locatie ( methode Files.createDirectory() ). Daarna verplaatsen we het bestand ( methode Files.move() ) van het bureaublad naar deze nieuwe map en ten slotte verwijderen we het bestand ( methode Files.delete() ). Console-uitvoer:

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
Opmerking:net als de methoden van de Pathinterface, retourneren veel methoden van deFilesPath klasse een object. De meeste methoden van de Filesklasse nemen ook Pathobjecten als invoer. Hier Paths.get()zal de methode je trouwe assistent zijn - maak er goed gebruik van. Waarin is nog meer interessant Files? Wat de oude Fileklasse echt miste, is een copy()methode! We hebben het er aan het begin van deze les over gehad. Nu is het tijd om het te ontmoeten!

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

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
Nu weet u hoe u bestanden programmatisch kunt kopiëren! Files:) Met de klasse kun je natuurlijk niet alleen een bestand zelf beheren, maar ook met de inhoud ervan werken. Het heeft de write()methode om gegevens naar een bestand te schrijven en alle 3 de methoden om gegevens te lezen: read(), readAllBytes(), en readAllLines() We zullen in detail stilstaan ​​bij de laatste. Waarom die? Omdat het een heel interessant retourtype heeft: List<String>! Dat wil zeggen, het retourneert ons een lijst met alle regels in het bestand. Dit maakt het natuurlijk erg handig om met de bestandsinhoud te werken, omdat het hele bestand regel voor regel bijvoorbeeld op de console kan worden weergegeven met een gewone forlus:

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);
       }
   }
}
Console-uitvoer:

I still recall the wondrous moment: 
When you appeared before my sight, 
As though a brief and fleeting omen, 
Pure phantom in enchanting light.
Superhandig! :) Deze mogelijkheid verscheen in Java 7. De Stream API verscheen in Java 8. Het voegt enkele elementen van functioneel programmeren toe aan Java. Inclusief uitgebreidere bestandsverwerkingsmogelijkheden. Stel je voor dat we de volgende taak hebben: zoek alle regels die beginnen met het woord "As", converteer ze naar HOOFDLETTERS en geef ze weer op de console. Hoe zou een oplossing die de Filesklasse gebruikt eruit zien in Java 7? Iets zoals dit:

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);
       }
   }
}
Console-uitvoer:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Missie volbracht, maar denk je niet dat onze code voor zo'n simpele taak een beetje... uitgebreid bleek te zijn? Met de Stream API van Java 8 ziet de oplossing er veel eleganter uit:

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);
   }
}
We bereikten hetzelfde resultaat, maar met veel minder code! Bovendien kan niemand zeggen dat we de "leesbaarheid" hebben verloren. Ik denk dat je gemakkelijk kunt reageren op wat deze code doet, zelfs zonder bekend te zijn met de Stream API. Kort gezegd is een Stream een ​​opeenvolging van elementen waarover je verschillende bewerkingen kunt uitvoeren. We krijgen een Stream-object van de Files.lines()methode en passen er vervolgens 3 functies op toe:
  1. We gebruiken de filter()methode om alleen die regels uit het bestand te selecteren die beginnen met "As".

  2. We doorlopen alle geselecteerde regels met behulp van de map()methode en zetten ze allemaal om in HOOFDLETTERS.

  3. We gebruiken de collect()methode om alle ontvangen regels te verzamelen in een List.

We krijgen dezelfde uitvoer:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Laten we nu terugkeren naar ons brood en boter, dat wil zeggen, bestanden :) De laatste mogelijkheid die we vandaag zullen overwegen, is door een bestandsboom lopen . In moderne besturingssystemen ziet de bestandsstructuur er meestal uit als een boom: het heeft een hoofdmap en er zijn vertakkingen, die andere vertakkingen kunnen hebben, enz. De hoofdmap en vertakkingen zijn mappen. De map " С:// " kan bijvoorbeeld de hoofdmap zijn. Het bevat twee takken: " C://Downloads " en " C://Users ". Elk van deze branches heeft twee branches: " C://Downloads/Pictures ", " C://Downloads/Video ", " C://Users/JohnSmith ", " C://Users/Pudge2005". En deze branches hebben op hun beurt weer andere branches, enz. en daarom noemen we het een boom. Op Linux is de structuur vergelijkbaar, maar de directory /Bestanden, Pad - 2 is de root. Stel je nu voor dat we moeten beginnen bij de rootdirectory , loop door al zijn mappen en submappen en vind bestanden met een bepaalde inhoud. We zoeken naar bestanden met de regel "Dit is het bestand dat we nodig hebben!" We nemen de map "testFolder", die op het bureaublad, als de hoofdmap Dit is de inhoud: Bestanden, Pad - 3De mappen niveau1-a en niveau1-b bevatten ook mappen: Bestanden, Pad - 4Bestanden, Pad - 5Er zijn geen mappen in deze "mappen op het tweede niveau", alleen individuele bestanden: Bestanden, pad - 6Bestanden, pad - 7De 3 bestanden met de inhoud die we nodig hebben, hebben bewust verklarende namen gekregen: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt. Dit zijn precies de bestanden die we met Java moeten vinden. Hoe doen we dit? Een zeer krachtige methode om door een bestandsboom te bladeren komt ons te hulp: Files.walkFileTree (). Dit is wat we moeten doen. Eerst hebben we een FileVisitor. FileVisitoris een speciale interface, waarin de methoden voor het doorlopen van een bestandsboom worden beschreven. Daar zullen we met name de logica plaatsen om de inhoud van een bestand te lezen en te controleren of het de tekst bevat die we nodig hebben. Zo ziet ons FileVisitoreruit:

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;
   }
}
In dit geval erft onze klasse SimpleFileVisitor. Dit is een klasse die implementeert FileVisitor, waarin we slechts één methode moeten overschrijven: visitFile(). Hier definiëren we wat er moet gebeuren met elk bestand in elke map. Als je meer complexe logica nodig hebt om de bestandsstructuur te doorlopen, moet je je eigen implementatie van FileVisitor. U zou nog 3 methoden in die klasse moeten implementeren:
  • preVisitDirectory(): de logica die moet worden uitgevoerd voordat een map wordt ingevoerd;

  • visitFileFailed(): de logica die moet worden uitgevoerd als een bestand niet kan worden bezocht (geen toegang of om andere redenen);

  • postVisitDirectory(): de logica die moet worden uitgevoerd na het invoeren van een map.

We hebben dergelijke logica niet nodig, dus we vinden het prima SimpleFileVisitor. De logica binnen de visitFile()methode is vrij eenvoudig: lees alle regels in het bestand, controleer of ze de inhoud bevatten die we nodig hebben, en zo ja, druk het absolute pad af op de console. De enige regel die u problemen zou kunnen bezorgen, is deze:

return FileVisitResult.CONTINUE;
Eigenlijk is dit heel eenvoudig. Hier beschrijven we gewoon wat het programma zou moeten doen nadat het bestand is bezocht en alle noodzakelijke bewerkingen zijn uitgevoerd. In ons geval willen we doorgaan met het doorkruisen van de boom, dus we kiezen voor de CONTINUEoptie. Maar als alternatief kunnen we een ander doel hebben: in plaats van alle bestanden te vinden die bevatten "Dit is het bestand dat we nodig hebben", vindt u slechts één zo'n bestand . Daarna zou het programma moeten eindigen. In dit geval zou onze code er precies hetzelfde uitzien, maar in plaats van pauze zou er zijn:

return FileVisitResult.TERMINATE;
Nou, laten we onze code uitvoeren en kijken of het werkt.

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

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
Uitstekend! Het werkte! :) Je zou ook deze kleine uitdaging kunnen aannemen: vervang SimpleFileVisitordoor een gewone FileVisitor, overschrijf alle 4 methoden en bedenk je eigen doel voor het programma. U kunt bijvoorbeeld een programma schrijven dat al zijn acties registreert: de naam van het bestand of de map weergeven voor of na het invoeren ervan. Dat is het voor nu. Tot snel! :)
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION