JavaRush /Java Blog /Random-IT /File Java, percorso

File Java, percorso

Pubblicato nel gruppo Random-IT
Ciao! Oggi parleremo di lavorare con file e directory. Sai già come gestire il contenuto dei file: avevamo molte lezioni dedicate a questo :) Penso che tu possa facilmente ricordare diverse lezioni necessarie 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 l'estensione File. Puoi leggere del suo lavoro qui . Ma in Java 7, i creatori del linguaggio hanno deciso di cambiare il modo in cui lavorano con file e directory. Ciò era dovuto al fatto che la classe Filepresentava una serie di svantaggi. Ad esempio, non disponeva di un metodo copy()che consentisse di copiare un file da una posizione a un'altra (una funzionalità apparentemente chiaramente necessaria). Inoltre, la classe Fileaveva molti metodi che restituivano booleanvalori. Se si verifica un errore, un metodo di questo tipo restituisce false anziché generare un'eccezione, il che rende molto difficile diagnosticare gli errori e determinarne le cause. Invece di una singola classe, Fileapparvero fino a 3 classi: Paths, Pathe Files. Beh, per essere precisi, Pathquesta è un'interfaccia, non una classe. Scopriamo come differiscono l'uno dall'altro e perché ognuno di essi è necessario. Cominciamo con la cosa più semplice: Paths.

Percorsi

Pathsè una classe molto semplice con un unico metodo statico get(). È stato creato esclusivamente per ottenere un oggetto di tipo dalla stringa o dall'URI passato Path. Non ha altre funzionalità. Ecco un esempio del suo 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ù difficile, vero? :) Bene, dato che abbiamo un oggetto di tipo Path, scopriamo cos'è Pathe perché è necessario :)

Sentiero

Path, nel complesso, è un analogo ridisegnato del File. È molto più facile lavorare con che con File. Innanzitutto , molti metodi di utilità (statici) sono stati rimossi e spostati nella classe Files. In secondo luogo , Pathsono stati ordinati i valori restituiti dai metodi. Nella classe, Filei metodi hanno restituito this String, that boolean, that File- non è stato facile capirlo. Ad esempio, esisteva un metodo getParent()che restituiva il percorso principale del file corrente come stringa. Ma allo stesso tempo esisteva un metodo getParentFile()che restituiva la stessa cosa, ma sotto forma di oggetto File! Ciò è chiaramente ridondante. Pertanto, nell'interfaccia, Pathil metodo getParent()e altri metodi per lavorare con i file restituiscono semplicemente un oggetto Path. Non ci sono un sacco di opzioni: tutto è facile e semplice. Quali metodi utili ha Path? Eccone alcuni ed esempi del loro lavoro:
  • getFileName()— restituisce il nome del file dal percorso;

  • getParent()— restituisce la directory “genitore” rispetto al percorso corrente (ovvero la directory più in alto nell'albero delle directory);

  • getRoot()— restituisce la directory “root”; cioè quello che si trova in cima all'albero delle directory;

  • startsWith(), endsWith()- 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:\Utenti\Nome utente\Desktop
    C:\
    true
    false

    Presta attenzione a come funziona il metodo endsWith(). Controlla se il percorso corrente termina con il percorso passato . È sul percorso e non sull'insieme dei personaggi .

    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:

    falsa
    verità

    È necessario passare al metodo il percorso completo endsWith(), e non solo un insieme di caratteri: altrimenti il ​​risultato sarà sempre false , anche se il percorso corrente termina effettivamente con tale sequenza di caratteri (come nel caso di “estFile.txt " nell'esempio sopra).

    Inoltre, esiste Pathun gruppo di metodi che semplifica il lavoro con percorsi assoluti (completi) e relativi .

Diamo un'occhiata a questi metodi:
  • boolean isAbsolute()— restituisce vero 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:

    VERO

  • Path normalize()— “normalizza” il percorso corrente, rimuovendo da esso gli elementi non necessari. Potresti sapere che i sistemi operativi più diffusi spesso utilizzano i caratteri "." per indicare i percorsi. (“directory corrente”) e “..” (directory principale). Ad esempio: " ./Pictures/dog.jpg " significa che nella directory in cui ci troviamo adesso, c'è una cartella Immagini e in essa c'è un file "dog.jpg"

    Quindi eccolo qui. Se il tuo programma ha un percorso che utilizza "." o “..”, il metodo normalize()li rimuoverà e otterrà un percorso che non li conterrà:

    
    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:\Utenti\Java\esempi
    C:\Utenti\esempi

  • Path relativize()— calcola il percorso relativo tra il percorso attuale e quello superato.

    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:

    Nome utente\Desktop\testFile.txt

L'elenco completo dei metodi Pathè piuttosto ampio. Li potete trovare tutti nella documentazione Oracle . Passiamo alla revisione Files.

File

Files- questa è una classe di utilità in cui sono stati spostati i metodi statici della classe File. Files- è più o meno uguale a Arrayso Collections, funziona solo con i file e non con array e raccolte :) È focalizzato sulla gestione di file e directory. Utilizzando metodi statici Files, possiamo creare, eliminare e spostare file e directory. Per queste operazioni vengono utilizzati i metodi 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 {

       //file creation
       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 directory
       Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));

       //move file from desktop to testDirectory. You need to move with the name of the file in the folder!
       testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Is our file left 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")));

       //remove 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 lì (metodo Files.createDirectory()). Successivamente spostiamo il file (metodo Files.move()) dal desktop in questa nuova cartella e alla fine eliminiamo il file (metodo Files.delete()). Output della console: il file è stato creato correttamente? true La directory è stata creata correttamente? true Il nostro file è ancora sul desktop? false Il nostro file è stato spostato in testDirectory? true Il file esiste ancora? falso Fai attenzione:Proprio come i metodi dell'interfaccia Path, molti metodi Filesrestituiscono un oggettoPath . La maggior parte dei metodi di classe Filesaccettano anche Path. Qui il metodo diventerà il tuo fedele assistente Paths.get(): usalo attivamente. Cos'altro c'è di interessante Files? Ciò che mancava davvero alla vecchia classe era il metodo ! File. copy()Abbiamo parlato di lui all'inizio della conferenza, 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 {

       //file creation
       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 directory
       Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
       System.out.println("Was the directory successfully created?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));

       //copy the file from the desktop to the directory testDirectory2.
       testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);

       System.out.println("Is our file left on the desktop?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));

       System.out.println("Has our file been copied to testDirectory?");
       System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
   }
}
Output della console: il file è stato creato correttamente? true La directory è stata creata correttamente? true Il nostro file è ancora sul desktop? true Il nostro file è stato copiato in testDirectory? true Ora è possibile copiare i file a livello di codice! :) Ma la classe Filesti consente non solo di gestire i file stessi, ma anche di lavorare con i suoi contenuti. Per scrivere dati in un file, ha un metodo write(), e per leggere - ben 3:, read()e su quest'ultimo readAllBytes()ci readAllLines() soffermeremo in dettaglio. Perché su di esso? Perché ha un tipo di ritorno molto interessante: List<String>! Cioè, ci restituisce un elenco di righe nel file. Naturalmente, questo rende molto comodo lavorare con il contenuto, perché l'intero file, riga per riga, può, ad esempio, essere inviato alla console in un ciclo regolare 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: Ricordo un momento meraviglioso: sei apparso davanti a me, come una visione fugace, come un genio di pura bellezza. Molto comodamente! :) Questa funzionalità è apparsa in Java 7. In Java 8 è apparsa l'API Stream , che ha aggiunto alcuni elementi di programmazione funzionale a Java. Incluse funzionalità più ricche per lavorare con i file. Immagina di avere un compito: trovare tutte le righe in un file che iniziano con la parola "Come", convertirle in MAIUSCOLO e inviarle alla console. Come sarebbe una soluzione che utilizza una classe Filesin 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("How")) {
               String upper = s.toUpperCase();
               result.add(upper);
           }
       }

       for (String s: result) {
           System.out.println(s);
       }
   }
}
Uscita console: COME UNA VISIONE DIGIUNATA, COME UN GENIO DI PURA BELLEZZA. Sembra che ce l'abbiamo fatta, ma non credi che per un compito così semplice il nostro codice sia risultato un po'... prolisso? Utilizzando l'API Java 8 Stream 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("How"))
               .map(String::toUpperCase)
               .collect(Collectors.toList());
       result.forEach(System.out::println);
   }
}
Abbiamo ottenuto lo stesso risultato, ma con molto meno codice! Inoltre, non si può dire che abbiamo perso in “leggibilità”. Penso che tu possa facilmente commentare cosa fa questo codice, anche se non hai familiarità con l'API Stream. Ma in sintesi uno Stream è una sequenza di elementi sui quali è possibile svolgere diverse funzioni. Otteniamo l'oggetto Stream dal metodo Files.lines()e quindi applichiamo 3 funzioni ad esso:
  1. Utilizzando il metodo, filter()selezioniamo solo quelle righe del file che iniziano con "Come".

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

  3. Combiniamo tutte le righe risultanti Listutilizzando il file collect().

All'uscita otteniamo lo stesso risultato: COME UNA VISIONE DIGIUNATA, COME UN GENIO DI PURA BELLEZZA. Se sei interessato a saperne di più sulle funzionalità di questa libreria, ti consigliamo di leggere questo articolo . Torneremo alle nostre pecore, cioè ai file :) L'ultima possibilità che prenderemo in considerazione oggi è camminare attraverso l'albero dei file . La struttura dei file nei moderni sistemi operativi assume molto spesso la forma di un albero: ha una radice e ci sono rami da cui possono essere separati altri rami, ecc. Le directory svolgono il ruolo di radice e rami. Ad esempio, la directory " C:// " può fungere da root . Da esso si dipartono due rami: “ C://Downloads ” e “ C://Users ”. Da ciascuno di questi rami ci sono altri 2 rami: “ C://Downloads/Pictures ”, “ C://Downloads/Video ”, “ C://Users/JohnSmith ”, “ C://Users/Pudge2005 ” . Da questi rami si diramano altri rami, ecc. - ecco come risulta un albero. In Linux sembra più o meno lo stesso, solo che lì la directory funge da root / File, percorso - 2 Ora immagina di avere un compito: conoscendo la directory root, dobbiamo esaminarla, cercare nelle cartelle di tutti i livelli e trovare i file al loro interno con il contenuto abbiamo bisogno. Cercheremo i file contenenti la riga "Questo è il file di cui abbiamo bisogno!" La nostra directory principale sarà la cartella "testFolder", che si trova sul desktop. Al suo interno ha il seguente contenuto: File, percorso - 3All'interno delle cartelle livello1-a e livello1-b ci sono anche delle cartelle: File, percorso - 4File, percorso - 5All'interno di queste “cartelle di secondo livello” non ci sono più cartelle, solo singoli file: File, percorso - 6File, percorso - 7Designeremo appositamente 3 file con il contenuto che bisogno con nomi chiari: FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt Questi sono quelli che dobbiamo trovare in base al contenuto utilizzando Java. Come possiamo farlo? Un metodo molto potente per attraversare un albero di file viene in soccorso: Files.walkFileTree(). Ecco cosa dobbiamo fare. Innanzitutto, abbiamo bisogno di FileVisitor. FileVisitorè un'interfaccia speciale che descrive tutti i metodi per attraversare l'albero dei file. Nello specifico, inseriremo la logica per leggere il contenuto del file e verificare se contiene il testo di cui abbiamo bisogno. Ecco come apparirà il nostro FileVisitor:

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("Required file found!");
               System.out.println(file.toAbsolutePath());
               break;
           }
       }

       return FileVisitResult.CONTINUE;
   }
}
In questo caso, la nostra classe eredita da SimpleFileVisitor. Questa è una classe che implementa FileVisitor, in cui è necessario sovrascrivere solo un metodo: visitFile(). Qui descriviamo cosa è necessario fare con ciascun file in ciascuna directory. Se hai bisogno di una logica di attraversamento più complessa, dovresti scrivere la tua implementazione FileVisitor. Lì dovrai implementare altri 3 metodi:
  • preVisitDirectory()— logica che deve essere eseguita prima di accedere alla cartella;

  • visitFileFailed()— cosa fare se l'accesso al file è impossibile (nessun accesso o altri motivi);

  • postVisitDirectory()— la logica che deve essere eseguita dopo l'accesso alla cartella.

Non abbiamo questa logica, quindi per noi è sufficiente SimpleFileVisitor. La logica all'interno del metodo visitFile()è abbastanza semplice: leggere tutte le righe del file, verificare se contengono il contenuto di cui abbiamo bisogno e, in tal caso, stampare il percorso assoluto della console. L'unica riga che potrebbe darti problemi è questa:

return FileVisitResult.CONTINUE;
In effetti, tutto è semplice. Qui descriviamo semplicemente cosa dovrebbe fare il programma dopo aver inserito il file e aver completato tutte le operazioni necessarie. Nel nostro caso, dobbiamo continuare ad attraversare l'albero, quindi scegliamo l'opzione CONTINUE. Ma noi, ad esempio, potremmo avere un altro compito: trovare non tutti i file che contengono "Questo è il file di cui abbiamo bisogno", ma solo uno di questi file . Successivamente è necessario terminare il programma. In questo caso, il nostro codice sarebbe esattamente lo stesso, ma invece di break; volevo:

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());
   }
}
Output della console: il file richiesto è stato trovato! C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt Il file richiesto è stato trovato! C:\Users\Username\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt Il file richiesto è stato trovato! C:\Users\Username\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt Ottimo, ce l'abbiamo fatta! :) Se vuoi saperne di più walkFileTree(), ti consiglio questo articolo . Puoi anche completare una piccola attività: sostituirla SimpleFileVisitorcon una normale FileVisitor, implementare tutti e 4 i metodi e trovare uno scopo per questo programma. Ad esempio, puoi scrivere un programma che registrerà tutte le tue azioni: visualizzerà il nome di un file o di una cartella nella console prima/dopo averli inseriti. Questo è tutto: ci vediamo più tardi! :)
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION