CodeGym /Java blogg /Slumpmässig /Java-filer, sökväg
John Squirrels
Nivå
San Francisco

Java-filer, sökväg

Publicerad i gruppen
Hej! Idag ska vi prata om att arbeta med filer och kataloger. Du vet redan hur man hanterar filinnehåll: vi har ägnat många lektioner åt detta :) Jag tror att du har lätt för att komma ihåg några klasser som används för dessa ändamål. I dagens lektion kommer vi att prata specifikt om filhantering: skapa, döpa om, etc. Före Java 7 utfördes alla sådana operationer med klassen File . Du kan läsa om det här . Men i Java 7 beslutade språkets skapare att ändra hur vi arbetar med filer och kataloger. Detta hände eftersom File -klassen hade flera nackdelar. Till exempel hade den inte metoden copy() , som skulle låta dig kopiera en fil från en plats till en annan (en till synes väsentlig förmåga). Dessutom harFilklassen hade en hel del metoder som returnerade booleska värden. När det finns ett fel returnerar en sådan metod false. Det ger inget undantag, vilket gör det mycket svårt att identifiera fel och diagnostisera deras orsaker. I stället för den enda File- klassen dök 3 klasser upp: Paths , Path , och Files . Tja, för att vara exakt är Path ett gränssnitt, inte en klass. Låt oss ta reda på hur de skiljer sig från varandra och varför vi behöver var och en av dem. Låt oss börja med det enklaste: Paths .

stigar

Paths är en mycket enkel klass med en enda statisk metod: get() . Det skapades enbart för att hämta ett sökvägsobjekt från den passerade strängen eller URI. Den har ingen annan funktion. Här är ett exempel på det på jobbet:

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");
   }
}
Inte den mest komplexa klassen, eller hur? :) Tja, vi har också den här typen Path . Låt oss ta reda på vad Path är och varför det behövs :)

Väg

Path är i stort sett en omdesignad analog av File -klassen. Det är mycket lättare att arbeta med än File . Först togs många verktyg (statiska) metoder ut och flyttades till klassen Files . För det andra ålades ordning på returvärdena för metoderna för Path -gränssnittet. I klassen File returnerade metoder antingen en String , eller en boolean , eller en File . Det var inte lätt att lista ut det. Det fanns till exempel en getParent() -metod som returnerade en sträng som representerar den överordnade sökvägen för den aktuella filen. Men det fanns också engetParentFile() -metoden, som returnerade samma sak men i form av ett File -objekt! Detta är helt klart överflödigt. Följaktligen returnerar getParent()-metoden och andra metoder för att arbeta med filer isökvägsgränssnittet helt enkelt ett sökvägsobjekt . Ingen hög med alternativ - allt är enkelt och enkelt. Vilka är några av de användbara metoderna som Path har? Här är några av dem och exempel på hur de fungerar:
  • getFileName() : returnerar filnamnet från sökvägen;

  • getParent() : returnerar "förälder"-katalogen för den aktuella sökvägen (med andra ord, katalogen som finns omedelbart ovanför i katalogträdet);

  • getRoot() : returnerar "root"-katalogen, dvs katalogen högst upp i katalogträdet;

  • startsWith() , endsWith() : kontrollera om sökvägen börjar/slutar med den passerade sökvägen:

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

    Konsolutgång:

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

    Var uppmärksam på hur endsWith() -metoden fungerar. Den kontrollerar om den aktuella sökvägen slutar med den passerade sökvägen . Specifikt om det är i sökvägen , inte i den passerade strängen .

    Jämför resultaten av dessa två samtal:

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

    Konsolutgång:

    
    false
    true
    

    EndsWith () -metoden måste skickas en äkta sökväg, inte bara en uppsättning tecken: annars kommer resultatet alltid att vara falskt, även om den aktuella sökvägen verkligen slutar med den teckensekvensen (som är fallet med "estFile.txt" " i exemplet ovan).

    Dessutom har Path en grupp metoder som förenklar arbetet med absoluta (fullständiga) och relativa vägar .

Låt oss titta på dessa metoder:
  • boolean isAbsolute() returnerar true om den aktuella sökvägen är 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());
       }
    }
    

    Konsolutgång:

    
    true
    
  • Path normalize() : "normaliserar" den aktuella sökvägen och tar bort onödiga element från den. Du kanske vet att i populära operativsystem symbolerna "." (nuvarande katalog) och ".." (överordnad katalog) används ofta för att ange sökvägar. Till exempel betyder " ./Pictures/dog.jpg " att den aktuella katalogen har en "Pictures"-mapp, som i sin tur innehåller en "dog.jpg"-fil.

    Titta här. Om en sökväg använder "." eller ".." visas i ditt program, kommer normalize() -metoden att ta bort dem och skapa en sökväg som inte innehåller 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());
       }
    }
    

    Konsolutgång:

    
    C:\Users\Java\examples
    C:\Users\examples
    
  • Path relativize() : beräknar den relativa vägen mellan den nuvarande och den passerade vägen.

    Till exempel:

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

    Konsolutgång:

    
    Username\Desktop\testFile.txt
    

Den kompletta listan över Path -metoder är ganska lång. Du hittar dem alla i Oracle-dokumentationen . Nu ska vi gå vidare till att överväga filer .

Filer

Files är en verktygsklass som innehåller de statiska metoder som tagits ut urklassen File . Filer är jämförbara med Arrays eller Collections . Skillnaden är att det fungerar med filer, inte arrayer eller samlingar :) Den fokuserar på att hantera filer och kataloger. Genom att använda de statiska metoderna i klassen Files kan vi skapa, ta bort och flytta filer och kataloger. Dessa operationer utförs med metoderna createFile() (för kataloger, createDirectory() ), move() och delete()- metoderna. Så här använder 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")));
   }
}
Här skapar vi först en fil ( metoden Files.createFile()) på skrivbordet. Sedan skapar vi en mapp på samma plats ( metoden Files.createDirectory()) . Efter det flyttar vi filen ( Files.move() -metoden) från skrivbordet till den här nya mappen, och slutligen tar vi bort filen ( Files.delete() -metoden). Konsolutgång:

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
Notera:liksom metoderna i Pathgränssnittet returnerar många metoder iFilesPath klassen ett objekt. De flesta av klassens metoder Filestar också Pathobjekt som indata. Här Paths.get()kommer metoden att vara din trogna assistent - använd den väl. Vad är mer intressant i Files? Vad den gamla Fileklassen verkligen saknade är en copy()metod! Vi pratade om det i början av den här lektionen. Nu är det dags att träffa den!

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")));
   }
}
Konsolutgång:

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 vet du hur man kopierar filer programmatiskt! :) Naturligtvis Fileslåter klassen dig inte bara hantera en fil själv, utan också arbeta med dess innehåll. Den har write()metoden för att skriva data till en fil, och alla tre metoder för att läsa data: , , read()och readAllBytes()Vi readAllLines() kommer att uppehålla oss i detalj vid den sista. Varför den där? Eftersom den har en väldigt intressant returtyp: List<String>! Det vill säga, den ger oss en lista över alla rader i filen. Detta gör det naturligtvis väldigt bekvämt att arbeta med filinnehållet, eftersom hela filen rad för rad kan till exempel visas på konsolen med en vanlig forloop:

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);
       }
   }
}
Konsolutgång:

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 bekvämt! :) Denna förmåga dök upp i Java 7. Stream API dök upp i Java 8. Den lägger till några delar av funktionell programmering till Java. Inklusive rikare filhanteringsmöjligheter. Föreställ dig att vi har följande uppgift: hitta alla rader som börjar med ordet "Som", konvertera dem till VERSALER och visa dem på konsolen. Hur skulle en lösning med Filesklassen se ut i Java 7? Något som det här:

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);
       }
   }
}
Konsolutgång:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Uppdraget fullbordat, men tycker du inte att vår kod för en så enkel uppgift visade sig vara lite... mångtydig? Med Java 8:s Stream API ser lösningen mycket mer elegant ut:

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 uppnådde samma resultat, men med mycket mindre kod! Dessutom kan ingen säga att vi har tappat "läsbarheten". Jag tror att du enkelt kan kommentera vad den här koden gör, även utan att vara bekant med Stream API. Kort sagt, en Stream är en sekvens av element, över vilka du kan utföra olika operationer. Vi får ett Stream-objekt från Files.lines()metoden och applicerar sedan 3 funktioner på det:
  1. Vi använder filter()metoden för att bara välja de rader från filen som börjar med "Som".

  2. Vi går igenom alla valda rader med map()metoden och konverterar var och en av dem till VERSALER.

  3. Vi använder collect()metoden för att samla alla mottagna rader till en List.

Vi får samma utdata:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Låt oss nu återgå till vårt bröd och smör, det vill säga filer :) Den sista möjligheten som vi kommer att överväga idag är att gå genom ett filträd . I moderna operativsystem ser filstrukturen oftast ut som ett träd: den har en rot och det finns grenar som kan ha andra grenar etc. Roten och grenarna är kataloger. Till exempel kan katalogen " С:// " vara roten. Den innehåller två grenar: " C://Nedladdningar " och " C://Users ". Var och en av dessa grenar har två grenar: " C://Downloads/Pictures ", " C://Downloads/Video ", " C://Users/JohnSmith ", " C://Users/Pudge2005". Och dessa grenar har i sin tur andra grenar etc. och det är därför vi kallar det ett träd. På Linux är strukturen liknande, men katalogen / är roten. Föreställ dig nu att vi måste börja vidFiler, sökväg - 2 rotkatalogen , gå igenom alla dess mappar och undermappar och hitta filer som har visst innehåll. Vi kommer att söka efter filer som innehåller raden "Detta är filen vi behöver!" Vi tar mappen "testFolder", som finns på skrivbordet, som rotkatalogen. Här är dess innehåll: Filer, sökväg - 3Mapparna nivå1-a och nivå1-b innehåller också mappar: Filer, sökväg - 4Filer, sökväg - 5Det finns inga mappar i dessa "andra nivå mappar", bara enskilda filer: Filer, sökväg - 6Filer, sökväg - 7De 3 filerna med det innehåll vi behöver får medvetet förklarande namn: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt. Det är just dessa filer vi behöver hitta med Java. Hur gör vi detta? En mycket kraftfull metod för att korsa ett filträd kommer till vår hjälp: Files.walkFileTree (). Här är vad vi behöver göra. Först behöver vi en FileVisitor. FileVisitorär ett speciellt gränssnitt, där metoderna för att korsa ett filträd beskrivs. Det är särskilt där vi kommer att lägga logiken för att läsa innehållet i en fil och kontrollera om den innehåller den text vi behöver. Så här FileVisitorser vårt ut:

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 det här fallet ärver vår klass SimpleFileVisitor. Det här är en klass som implementerar FileVisitor, där vi bara behöver åsidosätta en metod: visitFile(). Här definierar vi vad som behöver göras med varje fil i varje katalog. Om du behöver mer komplex logik för att korsa filstrukturen bör du skriva din egen implementering av FileVisitor. Du skulle behöva implementera ytterligare 3 metoder i den klassen:
  • preVisitDirectory(): logiken att köra innan man går in i en mapp;

  • visitFileFailed(): logiken att köra om en fil inte kan besökas (ingen åtkomst eller av andra skäl);

  • postVisitDirectory(): logiken att köra efter att du har angett en mapp.

Vi behöver inte köra någon sådan logik, så vi har det bra med SimpleFileVisitor. Logiken i visitFile()metoden är ganska enkel: läs alla rader i filen, kontrollera om de innehåller det innehåll vi behöver, och i så fall skriv ut den absoluta sökvägen på konsolen. Den enda raden som kan orsaka dig svårigheter är denna:

return FileVisitResult.CONTINUE;
Egentligen är detta väldigt enkelt. Här beskriver vi helt enkelt vad programmet ska göra efter att filen har besökts och alla nödvändiga operationer har utförts. I vårt fall vill vi fortsätta att korsa trädet, så vi väljer alternativet CONTINUE. Men alternativt kan vi ha ett annat mål: istället för att hitta alla filer som innehåller "Detta är filen vi behöver", hitta bara en sådan fil . Därefter bör programmet avslutas. I det här fallet skulle vår kod se exakt likadan ut, men istället för break skulle det finnas:

return FileVisitResult.TERMINATE;
Nåväl, låt oss köra vår kod och se om den fungerar.

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());
   }
}
Konsolutgång:

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
Excellent! Det fungerade! :) Du kan också anta den här lilla utmaningen: ersätt SimpleFileVisitormed en vanlig FileVisitor, åsidosätt alla fyra metoderna och kom på ditt eget syfte med programmet. Du kan till exempel skriva ett program som loggar alla dess åtgärder: visa namnet på filen eller mappen före eller efter att du anger dem. Det var allt tills vidare. Ses snart! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION