CodeGym/Java blog/Tilfældig/Java-filer, sti
John Squirrels
Niveau
San Francisco

Java-filer, sti

Udgivet i gruppen
Hej! I dag vil vi tale om at arbejde med filer og mapper. Du ved allerede, hvordan du administrerer filindhold: vi har dedikeret en masse lektioner til dette :) Jeg tror, ​​du finder det nemt at huske nogle få klasser, der bruges til disse formål. I dagens lektion taler vi specifikt om filhåndtering: oprettelse, omdøbning osv. Før Java 7 blev alle sådanne operationer udført ved hjælp af File- klassen. Du kan læse om det her . Men i Java 7 besluttede sprogets skabere at ændre, hvordan vi arbejder med filer og mapper. Dette skete, fordi File- klassen havde flere ulemper. For eksempel havde den ikke metoden copy() , som ville lade dig kopiere en fil fra et sted til et andet (en tilsyneladende væsentlig evne). Hertil kommerFilklassen havde en del metoder, der returnerede booleske værdier. Når der er en fejl, returnerer en sådan metode falsk. Det giver ikke en undtagelse, hvilket gør det meget vanskeligt at identificere fejl og diagnosticere deres årsager. I stedet for den enkelte filklasse dukkede 3 klasser op: Stier , Sti og Filer . Nå, for at være præcis, er Path en grænseflade, ikke en klasse. Lad os finde ud af, hvordan de adskiller sig fra hinanden, og hvorfor vi har brug for dem hver især. Lad os starte med det enkleste: Stier .

stier

Stier er en meget simpel klasse med en enkelt statisk metode: get() . Det blev udelukkende oprettet for at hente et stiobjekt fra den beståede streng eller URI. Den har ingen anden funktionalitet. Her er et eksempel på det på arbejdet:
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");
   }
}
Ikke den mest komplekse klasse, vel? :) Nå, vi har også fået denne Path- type. Lad os finde ud af, hvad Path er, og hvorfor det er nødvendigt :)

Sti

Path er i det store og hele en redesignet analog af File- klassen. Det er meget nemmere at arbejde med end File . Først blev mange hjælpemetoder (statiske) taget ud og flyttet til klassen Filer . For det andet blev der pålagt orden på returværdierne for metoderne til Path- grænsefladen. I klassen File returnerede metoder enten en streng eller en boolean eller en fil . Det var ikke nemt at finde ud af det. For eksempel var der en getParent()- metode, der returnerede en streng, der repræsenterer den overordnede sti til den aktuelle fil. Men der var også engetParentFile() -metoden, som returnerede det samme, men i form af et File- objekt! Dette er klart overflødigt. Derforreturnerer getParent()-metoden og andre metoder til at arbejde med filer i Path-grænsefladen blot et Path - objekt . Ingen bunke af muligheder - alt er nemt og enkelt. Hvad er nogle af de nyttige metoder, som Path har? Her er nogle af dem og eksempler på, hvordan de fungerer:
  • getFileName() : returnerer filnavnet fra stien;

  • getParent() : returnerer "overordnet"-biblioteket for den aktuelle sti (med andre ord biblioteket placeret umiddelbart ovenover i bibliotekstræet);

  • getRoot() : returnerer "root"-biblioteket, dvs. biblioteket øverst i mappetræet;

  • startsWith() , endsWith() : kontroller om stien starter/slutter med den beståede sti:

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

    Konsoludgang:

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

    Vær opmærksom på, hvordan endsWith()- metoden fungerer. Den kontrollerer, om den aktuelle sti ender med den passerede sti . Specifikt om det er i stien , ikke i den beståede streng .

    Sammenlign resultaterne af disse to opkald:

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

    Konsoludgang:

    false
    true

    endsWith ()- metoden skal sendes en ægte sti, ikke kun et sæt tegn: ellers vil resultatet altid være falsk, selvom den aktuelle sti virkelig ender med den sekvens af tegn (som det er tilfældet med "estFile.txt" " i eksemplet ovenfor).

    Derudover har Path en gruppe metoder, der forenkler arbejdet med absolutte (fulde) og relative stier .

Lad os se på disse metoder:
  • boolean isAbsolute() returnerer sand, hvis den aktuelle sti er absolut:

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

    Konsoludgang:

    true
  • Path normalize() : "normaliserer" den aktuelle sti og fjerner unødvendige elementer fra den. Du ved måske, at i populære operativsystemer symbolerne "." (nuværende bibliotek) og ".." (overordnet bibliotek) bruges ofte til at udpege stier. For eksempel betyder " ./Pictures/dog.jpg ", at den aktuelle mappe har en "Pictures"-mappe, som igen indeholder en "dog.jpg"-fil.

    Se her. Hvis en sti ved hjælp af "." eller ".." vises i dit program, vil normalize() metoden fjerne dem og producere en sti, der ikke indeholder dem:

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

    Konsoludgang:

    C:\Users\Java\examples
    C:\Users\examples
  • Path relativize() : beregner den relative sti mellem den nuværende og den beståede sti.

    For eksempel:

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

    Konsoludgang:

    Username\Desktop\testFile.txt

Den komplette liste over Path -metoder er ret lang. Du kan finde dem alle i Oracle-dokumentationen . Nu går vi videre til at overveje filer .

Filer

Files er en hjælpeklasse, der indeholder de statiske metoder, der er taget ud af File- klassen. Filer kan sammenlignes med Arrays eller Collections . Forskellen er, at det fungerer med filer, ikke arrays eller samlinger :) Det fokuserer på at administrere filer og mapper. Ved at bruge de statiske metoder i klassen Filer kan vi oprette, slette og flytte filer og mapper. Disse operationer udføres ved hjælp af createFile() (for mapper, createDirectory() ), move() og delete() metoderne. Sådan bruger du dem:
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")));
   }
}
Her opretter vi først en fil ( Files.createFile() metode) på skrivebordet. Derefter opretter vi en mappe på samme placering ( Files.createDirectory() metoden). Derefter flytter vi filen ( Files.move()- metoden) fra skrivebordet til denne nye mappe, og til sidst sletter vi filen ( Files.delete()- metoden). Konsoludgang:
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
Bemærk:ligesom metoderne i grænsefladen Path, returnerer mange metoder iFilesPath klassen et objekt. De fleste af klassens metoder Filestager også Pathobjekter som input. Her Paths.get()vil metoden være din trofaste assistent - gør god brug af den. Hvad er der ellers interessant i Files? Hvad den gamle Fileklasse virkelig manglede, er en copy()metode! Vi talte om det i begyndelsen af ​​denne lektion. Nu er det tid til at møde det!
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")));
   }
}
Konsoludgang:
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 ved du, hvordan du kopierer filer programmatisk! :) Selvfølgelig Fileslader klassen dig ikke kun administrere en fil selv, men også arbejde med dens indhold. Den har write()metoden til at skrive data til en fil, og alle 3 metoder til at læse data: read(), readAllBytes(), og readAllLines() Vi vil dvæle i detaljer ved den sidste. Hvorfor den? Fordi den har en meget interessant returtype: List<String>! Det vil sige, at det returnerer os en liste over alle linjerne i filen. Det gør det selvfølgelig meget bekvemt at arbejde med filindholdet, fordi hele filen, linje for linje, for eksempel kan vises på konsollen ved hjælp af en almindelig loop 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);
       }
   }
}
Konsoludgang:
I still recall the wondrous moment:
When you appeared before my sight,
As though a brief and fleeting omen,
Pure phantom in enchanting light.
Super praktisk! :) Denne evne dukkede op i Java 7. Stream API'et dukkede op i Java 8. Det tilføjer nogle elementer af funktionel programmering til Java. Inklusiv rigere filhåndteringsmuligheder. Forestil dig, at vi har følgende opgave: find alle de linjer, der begynder med ordet "Som", konverter dem til STORE BOGSTAVER, og vis dem på konsollen. Hvordan ville en løsning med Filesklassen se ud i Java 7? Noget som dette:
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);
       }
   }
}
Konsoludgang:
AS THOUGH A BRIEF AND FLEETING OMEN,
PURE PHANTOM IN ENCHANTING LIGHT.
Mission fuldført, men synes du ikke, at vores kode til en så simpel opgave viste sig at være lidt... verbose? Ved at bruge Java 8's Stream API ser løsningen meget mere elegant ud:
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);
   }
}
Vi opnåede det samme resultat, men med meget mindre kode! Hvad mere er, kan ingen sige, at vi har mistet "læsbarheden". Jeg tror, ​​du nemt kan kommentere på, hvad denne kode gør, selv uden at være bekendt med Stream API. Kort sagt er en Stream en sekvens af elementer, som du kan udføre forskellige operationer over. Vi får et Stream-objekt fra Files.lines()metoden og anvender derefter 3 funktioner på det:
  1. Vi bruger filter()metoden til kun at vælge de linjer fra filen, der begynder med "Som".

  2. Vi går gennem alle de valgte linjer ved hjælp af map()metoden og konverterer hver af dem til STORE BOGSTAVER.

  3. Vi bruger collect()metoden til at samle alle de modtagne linjer i en List.

Vi får det samme output:
AS THOUGH A BRIEF AND FLEETING OMEN,
PURE PHANTOM IN ENCHANTING LIGHT.
Lad os nu vende tilbage til vores brød og smør, det vil sige filer :) Den sidste mulighed, som vi vil overveje i dag, er at gå gennem et filtræ . I moderne operativsystemer ligner filstrukturen oftest et træ: den har en rod, og der er grene, som kan have andre grene osv. Roden og grenene er mapper. For eksempel kan biblioteket " С:// " være roden. Det inkluderer to grene: " C://Downloads " og " C://Users ". Hver af disse grene har to grene: " C://Downloads/Pictures ", " C://Downloads/Video ", " C://Users/JohnSmith ", " C://Users/Pudge2005". Og disse grene har igen andre grene osv., og det er derfor, vi kalder det et træ. På Linux er strukturen ens, men mappen / er roden. Forestil dig nu, at vi skal starte vedFiler, sti - 2 rodmappen , gå gennem alle dens mapper og undermapper og find filer, der har noget bestemt indhold. Vi søger efter filer, der indeholder linjen "Dette er den fil, vi skal bruge!" Vi tager mappen "testFolder", som er på skrivebordet, som rodmappen Her er indholdet: Filer, sti - 3Mapperne niveau1-a og niveau1-b indeholder også mapper: Filer, sti - 4Filer, sti - 5Der er ingen mapper i disse "mapper på andet niveau", kun individuelle filer: Filer, sti - 6Filer, sti - 7De 3 filer med det indhold, vi har brug for, får bevidst forklarende navne: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt. Det er netop de filer, vi skal finde ved hjælp af Java. Hvordan gør vi dette? En meget kraftfuld metode til at krydse et filtræ kommer os til hjælp: Files.walkFileTree (). Her er hvad vi skal gøre. For det første har vi brug for en FileVisitor. FileVisitorer en speciel grænseflade, hvor metoderne til at krydse et filtræ er beskrevet. Det er især der, vi vil placere logikken for at læse indholdet af en fil og kontrollere, om den indeholder den tekst, vi har brug for. Sådan FileVisitorser vores ud:
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;
   }
}
I dette tilfælde arver vores klasse SimpleFileVisitor. Dette er en klasse, der implementerer FileVisitor, hvor vi kun skal tilsidesætte én metode: visitFile(). Her definerer vi, hvad der skal gøres med hver fil i hver mappe. Hvis du har brug for mere kompleks logik til at krydse filstrukturen, bør du skrive din egen implementering af FileVisitor. Du skal implementere yderligere 3 metoder i den klasse:
  • preVisitDirectory(): logikken, der skal udføres før indtastning af en mappe;

  • visitFileFailed(): logikken til at udføre, hvis en fil ikke kan besøges (ingen adgang eller af andre årsager);

  • postVisitDirectory(): logikken, der skal udføres efter indtastning af en mappe.

Vi behøver ikke udføre nogen sådan logik, så vi har det fint med SimpleFileVisitor. Logikken i visitFile()metoden er ret enkel: læs alle linjerne i filen, tjek om de indeholder det indhold, vi har brug for, og i så fald udskriv den absolutte sti på konsollen. Den eneste linje, der kan give dig problemer, er denne:
return FileVisitResult.CONTINUE;
Faktisk er dette meget simpelt. Her beskriver vi blot, hvad programmet skal gøre, efter at filen er besøgt, og alle nødvendige handlinger er udført. I vores tilfælde ønsker vi at fortsætte med at krydse træet, så vi vælger muligheden CONTINUE. Men alternativt kan vi have et andet mål: i stedet for at finde alle filer, der indeholder "Dette er den fil, vi har brug for", skal du kun finde én sådan fil . Derefter bør programmet afsluttes. I dette tilfælde ville vores kode se nøjagtig det samme ud, men i stedet for pause ville der være:
return FileVisitResult.TERMINATE;
Nå, lad os køre vores kode og se, om det virker.
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());
   }
}
Konsoludgang:
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
Fremragende! Det virkede! :) Du kunne også acceptere denne lille udfordring: udskift SimpleFileVisitormed en almindelig FileVisitor, tilsidesæt alle 4 metoder, og kom med dit eget formål med programmet. For eksempel kan du skrive et program, der logger alle dets handlinger: vis navnet på filen eller mappen før eller efter indtastning af dem. Det er alt for nu. Vi ses snart! :)
Kommentarer
  • Populær
  • Ny
  • Gammel
Du skal være logget ind for at skrive en kommentar
Denne side har ingen kommentarer endnu