CodeGym /Java Blog /Random-IT /File Java, percorso
John Squirrels
Livello 41
San Francisco

File Java, percorso

Pubblicato nel gruppo Random-IT
CIAO! Oggi parleremo di come lavorare con file e directory. Sai già come gestire i contenuti dei file: abbiamo dedicato molte lezioni a questo :) Penso che trovi facile ricordare alcune classi utilizzate per questi scopi. Nella lezione di oggi parleremo specificamente della gestione dei file: creazione, ridenominazione, ecc. Prima di Java 7, tutte queste operazioni venivano eseguite utilizzando la classe File . Puoi leggerlo qui . Ma in Java 7, i creatori del linguaggio hanno deciso di cambiare il modo in cui lavoriamo con file e directory. Ciò è accaduto perché la classe File presentava diversi inconvenienti. Ad esempio, non aveva il metodo copy() , che ti permetteva di copiare un file da una posizione a un'altra (un'abilità apparentemente essenziale). Inoltre, ilLa classe File aveva alcuni metodi che restituiscono valori booleani . Quando c'è un errore, tale metodo restituisce false. Non genera un'eccezione, rendendo molto difficile identificare gli errori e diagnosticarne le cause. Al posto della singola classe File , sono apparse 3 classi: Paths , Path e Files . Bene, per essere precisi, Path è un'interfaccia, non una classe. Scopriamo come differiscono l'uno dall'altro e perché abbiamo bisogno di ciascuno di essi. Cominciamo con il più semplice: Paths .

Percorsi

Paths è una classe molto semplice con un unico metodo statico: get() . È stato creato esclusivamente per ottenere un oggetto Path dalla stringa passata o dall'URI. Non ha altre funzionalità. Ecco un esempio al lavoro:

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");
   }
}
Non la classe più complessa, giusto? :) Beh, abbiamo anche questo tipo Path . Scopriamo cos'è Path e perché è necessario :)

Sentiero

Path , in generale, è un analogo ridisegnato della classe File . È molto più facile lavorare con File . Innanzitutto , molti metodi di utilità (statici) sono stati eliminati e spostati nella classe Files . In secondo luogo , l'ordine è stato imposto ai valori di ritorno dei metodi dell'interfaccia Path . Nella classe File , i metodi hanno restituito un String , un boolean o un File . Non è stato facile capirlo. Ad esempio, c'era un metodo getParent() che restituiva una stringa che rappresentava il percorso padre del file corrente. Ma c'era anche ungetParentFile() , che ha restituito la stessa cosa ma sotto forma di unoggetto File ! Questo è chiaramente ridondante. Di conseguenza, nell'interfaccia Path , il metodo getParent() e altri metodi per lavorare con i file restituiscono semplicemente unoggetto Path . Nessuna pila di opzioni: tutto è facile e semplice. Quali sono alcuni dei metodi utili che Path ha? Eccone alcuni ed esempi di come funzionano:
  • getFileName() : restituisce il nome del file dal percorso;

  • getParent() : restituisce la directory "padre" del percorso corrente (in altre parole, la directory che si trova immediatamente sopra nell'albero delle directory);

  • getRoot() : restituisce la directory "root", cioè la directory in cima all'albero delle directory;

  • startWith() , endWith() : controlla se il percorso inizia/termina con il percorso passato:

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

    Uscita console:

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

    Presta attenzione a come funziona il metodo endsWith() . Controlla se il percorso corrente termina con il percorso passato . In particolare, se si trova nel path , non nella stringa passata .

    Confronta i risultati di queste due chiamate:

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

    Uscita console:

    
    false
    true
    

    Al metodo endsWith() deve essere passato un vero e proprio percorso, non solo un insieme di caratteri: in caso contrario, il risultato sarà sempre falso, anche se il percorso corrente termina davvero con quella sequenza di caratteri (come nel caso di "estFile.txt " nell'esempio precedente).

    Inoltre, Path dispone di un gruppo di metodi che semplifica il lavoro con percorsi assoluti (completi) e relativi .

Diamo un'occhiata a questi metodi:
  • boolean isAbsolute() restituisce true se il percorso corrente è assoluto:

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

    Uscita console:

    
    true
    
  • Path normalize() : "normalizza" il percorso corrente, rimuovendo gli elementi non necessari da esso. Potresti sapere che nei sistemi operativi più diffusi i simboli "." (directory corrente) e ".." (directory padre) sono spesso usati per designare i percorsi. Ad esempio, " ./Immagini/cane.jpg " significa che la directory corrente ha una cartella "Immagini", che a sua volta contiene un file "cane.jpg".

    Guarda qui. Se un percorso utilizza "." o ".." appare nel tuo programma, il metodo normalize() li rimuoverà e produrrà un percorso che non li contiene:

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

    Uscita console:

    
    C:\Users\Java\examples
    C:\Users\examples
    
  • Path relativize() : calcola il percorso relativo tra il percorso corrente e quello passato.

    Per esempio:

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

    Uscita console:

    
    Username\Desktop\testFile.txt
    

L'elenco completo dei metodi Path è piuttosto lungo. Puoi trovarli tutti nella documentazione di Oracle . Ora passeremo a considerare Files .

File

Files è una classe di utilità che contiene i metodi statici estratti dalla classe File . Files è paragonabile a Arrays o Collections . La differenza è che funziona con i file, non con gli array o le raccolte :) Si concentra sulla gestione di file e directory. Utilizzando i metodi statici della classe Files , possiamo creare, eliminare e spostare file e directory. Queste operazioni vengono eseguite utilizzando imetodi createFile() (per le directory, createDirectory() ), move() e delete() . Ecco come usarli:

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")));
   }
}
Qui creiamo prima un file ( metodo Files.createFile() ) sul desktop. Quindi creiamo una cartella nella stessa posizione ( metodo Files.createDirectory() ). Successivamente, spostiamo il file ( metodo Files.move() ) dal desktop a questa nuova cartella e infine eliminiamo il file ( metodo Files.delete() ). Uscita console:

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
Nota:come i metodi dell'interfaccia Path, molti metodi della Filesclasse restituiscono unPath oggetto. La maggior parte dei metodi della Filesclasse accetta anche Pathoggetti come input. Qui il Paths.get()metodo sarà il tuo fedele assistente: fanne buon uso. Cos'altro c'è di interessante Files? FileCiò che mancava davvero alla vecchia classe è un copy()metodo! Ne abbiamo parlato all'inizio di questa lezione. Ora è il momento di incontrarlo!

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

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
Ora sai come copiare i file a livello di codice! :) Naturalmente, la Filesclasse ti consente non solo di gestire un file stesso, ma anche di lavorare con il suo contenuto. Ha il write()metodo per scrivere i dati su un file e tutti e 3 i metodi per leggere i dati: read(), readAllBytes(), e readAllLines() Ci soffermeremo in dettaglio sull'ultimo. Perché quello? Perché ha un tipo di ritorno molto interessante: List<String>! Cioè, ci restituisce un elenco di tutte le righe nel file. Naturalmente, questo rende molto comodo lavorare con il contenuto del file, perché l'intero file, riga per riga, può, ad esempio, essere visualizzato sulla console utilizzando un normale ciclo 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);
       }
   }
}
Uscita console:

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 conveniente! :) Questa capacità è apparsa in Java 7. L' API Stream è apparsa in Java 8. Aggiunge alcuni elementi di programmazione funzionale a Java. Comprese funzionalità di gestione dei file più ricche. Immagina di avere il seguente compito: trova tutte le righe che iniziano con la parola "As", convertile in MAIUSCOLO e visualizzale sulla console. Come sarebbe una soluzione che utilizza la Filesclasse in Java 7? Qualcosa come questo:

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);
       }
   }
}
Uscita console:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Missione compiuta, ma non credi che per un compito così semplice il nostro codice si sia rivelato un po'... prolisso? Utilizzando l'API Stream di Java 8, la soluzione sembra molto più elegante:

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);
   }
}
Abbiamo ottenuto lo stesso risultato, ma con molto meno codice! Inoltre, nessuno può dire che abbiamo perso la "leggibilità". Penso che tu possa facilmente commentare cosa fa questo codice, anche senza avere familiarità con l'API Stream. In breve, uno Stream è una sequenza di elementi, sui quali è possibile eseguire varie operazioni. Otteniamo un oggetto Stream dal Files.lines()metodo, quindi applichiamo 3 funzioni ad esso:
  1. Usiamo il filter()metodo per selezionare solo quelle righe dal file che iniziano con "As".

  2. Esaminiamo tutte le righe selezionate utilizzando il map()metodo e convertiamo ciascuna di esse in MAIUSCOLO.

  3. Usiamo il collect()metodo per raccogliere tutte le righe ricevute in un file List.

Otteniamo lo stesso output:

AS THOUGH A BRIEF AND FLEETING OMEN, 
PURE PHANTOM IN ENCHANTING LIGHT.
Ora torniamo al nostro pane quotidiano, cioè ai file :) L'ultima capacità che prenderemo in considerazione oggi è l' esplorazione di un file tree . Nei moderni sistemi operativi, la struttura dei file ha spesso l'aspetto di un albero: ha una radice e ci sono rami, che possono avere altri rami, ecc. La radice e i rami sono directory. Ad esempio, la directory " С:// " potrebbe essere la radice. Comprende due rami: " C://Downloads " e " C://Users ". Ognuno di questi rami ha due rami: " C://Downloads/Pictures ", " C://Downloads/Video ", " C://Users/JohnSmith ", " C://Users/Pudge2005". E questi rami a loro volta hanno altri rami, ecc. ed è per questo che lo chiamiamo albero. Su Linux, la struttura è simile, ma la directory /File, Percorso - 2 è la radice. Ora immagina di dover iniziare dalla directory radice , esamina tutte le sue cartelle e sottocartelle e trova i file con un contenuto particolare. Cercheremo i file che contengono la riga "Questo è il file di cui abbiamo bisogno!" Prenderemo la cartella "testFolder", che si trova su il desktop, come directory principale.Ecco il suo contenuto: File, Percorso - 3Le cartelle level1-a e level1-b contengono anche cartelle: File, percorso - 4File, percorso - 5Non ci sono cartelle in queste "cartelle di secondo livello", solo singoli file: File, percorso - 6File, Percorso - 7Ai 3 file con i contenuti di cui abbiamo bisogno vengono deliberatamente dati nomi esplicativi: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt. Questi sono esattamente i file che dobbiamo trovare usando Java. Come facciamo questo? Ci viene in aiuto un metodo molto potente per attraversare un albero di file: Files.walkFileTree (). Ecco cosa dobbiamo fare. Innanzitutto, abbiamo bisogno di un file FileVisitor. FileVisitorè un'interfaccia speciale, in cui sono descritti i metodi per attraversare un albero di file. In particolare, è lì che metteremo la logica per leggere il contenuto di un file e controllare se contiene il testo di cui abbiamo bisogno. Ecco come FileVisitorappare il nostro:

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 questo caso, la nostra classe eredita SimpleFileVisitor. Questa è una classe che implementa FileVisitor, in cui dobbiamo eseguire l'override di un solo metodo: visitFile(). Qui definiamo cosa deve essere fatto con ogni file in ogni directory. Se hai bisogno di una logica più complessa per attraversare la struttura del file, dovresti scrivere la tua implementazione di FileVisitor. Dovresti implementare altri 3 metodi in quella classe:
  • preVisitDirectory(): la logica da eseguire prima di entrare in una cartella;

  • visitFileFailed(): la logica da eseguire se un file non può essere visitato (nessun accesso o per altri motivi);

  • postVisitDirectory(): la logica da eseguire dopo essere entrati in una cartella.

Non abbiamo bisogno di alcuna logica di questo tipo eseguita, quindi stiamo bene con SimpleFileVisitor. La logica all'interno del visitFile()metodo è piuttosto semplice: leggi tutte le righe nel file, controlla se contengono il contenuto di cui abbiamo bisogno e, in tal caso, stampa il percorso assoluto sulla console. L'unica riga che potrebbe causarti difficoltà è questa:

return FileVisitResult.CONTINUE;
In realtà, questo è molto semplice. Qui stiamo semplicemente descrivendo cosa dovrebbe fare il programma dopo che il file è stato visitato e tutte le operazioni necessarie sono state eseguite. Nel nostro caso, vogliamo continuare ad attraversare l'albero, quindi scegliamo l' CONTINUEopzione. Ma, in alternativa, potremmo avere un obiettivo diverso: invece di trovare tutti i file che contengono "Questo è il file di cui abbiamo bisogno", trova solo uno di questi file . Successivamente, il programma dovrebbe terminare. In questo caso, il nostro codice sembrerebbe esattamente lo stesso, ma invece di break ci sarebbe:

return FileVisitResult.TERMINATE;
Bene, eseguiamo il nostro codice e vediamo se funziona.

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

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
Eccellente! Ha funzionato! :) Potresti anche accettare questa piccola sfida: sostituire SimpleFileVisitorcon un normale FileVisitor, sovrascrivere tutti e 4 i metodi e trovare il tuo scopo per il programma. Ad esempio, potresti scrivere un programma che registri tutte le sue azioni: visualizzare il nome del file o della cartella prima o dopo averli inseriti. È tutto per ora. Arrivederci! :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION