Caminos
Paths es una clase muy simple con un único método estático: get() . Fue creado únicamente para obtener un objeto Path de la cadena o URI pasada. No tiene otra funcionalidad. Aquí hay un ejemplo de esto en el trabajo:
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");
}
}
No es la clase más compleja, ¿verdad? :) Bueno, también tenemos este tipo de ruta . Averigüemos qué es Path y por qué es necesario :)
Camino
Path , en general, es un análogo rediseñado de la clase File . Es mucho más fácil trabajar con File . En primer lugar , se eliminaron muchos métodos de utilidad (estáticos) y se trasladaron a la clase Archivos . En segundo lugar , se impuso orden en los valores de retorno de los métodos de la interfaz Path . En la clase File , los métodos devolvían un String , un booleano o un File . No fue fácil averiguarlo. Por ejemplo, había un método getParent() que devolvía una cadena que representaba la ruta principal del archivo actual. Pero también hubo unmétodo getParentFile() , que devolvió lo mismo pero en forma de unobjeto de archivo . Esto es claramente redundante. En consecuencia, en lainterfaz Path , el método getParent() y otros métodos para trabajar con archivos simplemente devuelven un objeto Path . Sin montones de opciones: todo es fácil y simple. ¿ Cuáles son algunos de los métodos útiles que tiene Path ? Estos son algunos de ellos y ejemplos de cómo funcionan:-
getFileName() : devuelve el nombre del archivo de la ruta;
-
getParent() : devuelve el directorio "principal" de la ruta actual (en otras palabras, el directorio ubicado inmediatamente arriba en el árbol de directorios);
-
getRoot() : devuelve el directorio "raíz", es decir, el directorio en la parte superior del árbol de directorios;
-
empieza con() , termina con() : compruebe si la ruta comienza/termina con la ruta pasada:
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); } }
Salida de la consola:
testFile.txt C:\Users\Username\Desktop C:\ true false
Preste atención a cómo funciona el método extremosCon() . Comprueba si la ruta actual termina con la ruta pasada . Específicamente, si está en la ruta , no en la cadena pasada .
Compare los resultados de estas dos llamadas:
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")); } }
Salida de la consola:
false true
Al método EndsWith() se le debe pasar una ruta genuina, no solo un conjunto de caracteres: de lo contrario, el resultado siempre será falso, incluso si la ruta actual realmente termina con esa secuencia de caracteres (como es el caso de "estFile.txt " en el ejemplo anterior).
Además, Path tiene un grupo de métodos que simplifica el trabajo con rutas absolutas (completas) y relativas .
-
boolean isAbsolute() devuelve verdadero si la ruta actual es absoluta:
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()); } }
Salida de la consola:
true
-
Path normalize() : "normaliza" la ruta actual, eliminando elementos innecesarios de ella. Es posible que sepa que en los sistemas operativos populares los símbolos "." (directorio actual) y ".." (directorio principal) se utilizan a menudo para designar rutas. Por ejemplo, " ./Imágenes/perro.jpg " significa que el directorio actual tiene una carpeta "Imágenes", que a su vez contiene un archivo "perro.jpg".
Mira aquí. Si una ruta usando "." o ".." aparece en su programa, el método normalize() los eliminará y producirá una ruta que no los 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()); } }
Salida de la consola:
C:\Users\Java\examples C:\Users\examples
-
Path relativize() : calcula la ruta relativa entre la ruta actual y la pasada.
Por ejemplo:
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)); } }
Salida de la consola:
Username\Desktop\testFile.txt
archivos
Archivos es una clase de utilidad que contiene los métodos estáticos extraídos de la clase Archivo . Files es comparable a Arrays o Collections . La diferencia es que funciona con archivos, no con arreglos o colecciones :) Se enfoca en administrar archivos y directorios. Usando los métodos estáticos de la clase Files , podemos crear, eliminar y mover archivos y directorios. Estas operaciones se realizan utilizando losmétodos createFile() (para directorios, createDirectory() ), move() y delete() . He aquí cómo usarlos:
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")));
}
}
Aquí primero creamos un archivo ( método Files.createFile() ) en el escritorio. Luego creamos una carpeta en la misma ubicación ( método Files.createDirectory() ). Después de eso, movemos el archivo ( método Files.move() ) desde el escritorio a esta nueva carpeta, y finalmente eliminamos el archivo ( método Files.delete() ). Salida de la consola:
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:al igual que los métodos de la Path
interfaz, muchos métodos de la Files
clase devuelven unPath
objeto. La mayoría de los métodos de la Files
clase también toman Path
objetos como entradas. Aquí el Paths.get()
método será su fiel ayudante: utilícelo bien. ¿ Qué más es interesante en Files
? File
¡Lo que realmente le faltaba a la vieja clase es un copy()
método! Hablamos de ello al principio de esta lección. ¡Ahora es el momento de conocerlo!
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")));
}
}
Salida de la consola:
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
¡Ahora sabe cómo copiar archivos mediante programación! :) Por supuesto, la Files
clase le permite no solo administrar un archivo en sí mismo, sino también trabajar con su contenido. Tiene el write()
método para escribir datos en un archivo, y los 3 métodos para leer datos: read()
, readAllBytes()
y readAllLines()
Nos detendremos en detalle en el último. ¿Por qué ese? Porque tiene un tipo de retorno muy interesante: List<String>
! Es decir, nos devuelve una lista de todas las líneas del archivo. Por supuesto, esto hace que sea muy conveniente trabajar con el contenido del archivo, porque el archivo completo, línea por línea, puede, por ejemplo, mostrarse en la consola usando un bucle normal 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);
}
}
}
Salida de la consola:
I still recall the wondrous moment:
When you appeared before my sight,
As though a brief and fleeting omen,
Pure phantom in enchanting light.
¡Súper conveniente! :) Esta habilidad apareció en Java 7. La API Stream apareció en Java 8. Agrega algunos elementos de programación funcional a Java. Incluyendo capacidades de manejo de archivos más ricas. Imagina que tenemos la siguiente tarea: encontrar todas las líneas que comienzan con la palabra "As", convertirlas a MAYÚSCULAS y mostrarlas en la consola. ¿ Cómo sería una solución usando la Files
clase en Java 7? Algo como esto:
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);
}
}
}
Salida de la consola:
AS THOUGH A BRIEF AND FLEETING OMEN,
PURE PHANTOM IN ENCHANTING LIGHT.
Misión cumplida, pero ¿no crees que para una tarea tan simple nuestro código resultó ser un poco... prolijo? Usando la API Stream de Java 8, la solución se ve mucho más 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);
}
}
Logramos el mismo resultado, ¡pero con mucho menos código! Es más, nadie puede decir que hemos perdido "legibilidad". Creo que puede comentar fácilmente lo que hace este código, incluso sin estar familiarizado con Stream API. En resumen, un Stream es una secuencia de elementos, sobre los cuales se pueden realizar diversas operaciones. Obtenemos un objeto Stream del Files.lines()
método y luego le aplicamos 3 funciones:
-
Usamos el
filter()
método para seleccionar solo aquellas líneas del archivo que comienzan con "As". -
Recorremos todas las líneas seleccionadas usando el
map()
método y convertimos cada una de ellas a MAYÚSCULAS. -
Usamos el
collect()
método para reunir todas las líneas recibidas en un archivoList
.
AS THOUGH A BRIEF AND FLEETING OMEN,
PURE PHANTOM IN ENCHANTING LIGHT.
Ahora volvamos a nuestro pan y mantequilla, es decir, archivos :) La última capacidad que consideraremos hoy es caminar a través de un árbol de archivos . En los sistemas operativos modernos, la estructura de archivos suele parecerse a un árbol: tiene una raíz y ramas, que pueden tener otras ramas, etc. La raíz y las ramas son directorios. Por ejemplo, el directorio " С:// " puede ser la raíz. Incluye dos ramas: " C://Descargas " y " C://Usuarios ". Cada una de estas ramas tiene dos ramas: " C://Descargas/Imágenes ", " C://Descargas/Video ", " C://Usuarios/JohnSmith ", " C://Usuarios/Pudge2005". Y estas ramas a su vez tienen otras ramas, etc. y es por eso que lo llamamos árbol. En Linux, la estructura es similar, pero el directorio / es la raíz. Ahora imagina





Files.walkFileTree ()
. Esto es lo que tenemos que hacer. Primero, necesitamos un FileVisitor
. FileVisitor
es una interfaz especial, en la que se describen los métodos para recorrer un árbol de archivos. En particular, ahí es donde colocaremos la lógica para leer el contenido de un archivo y verificar si contiene el texto que necesitamos. Así es como FileVisitor
se ve nuestro:
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;
}
}
En este caso, nuestra clase hereda SimpleFileVisitor
. Esta es una clase que implementa FileVisitor
, en la que necesitamos anular solo un método: visitFile()
. Aquí definimos lo que se debe hacer con cada archivo en cada directorio. Si necesita una lógica más compleja para atravesar la estructura de archivos, debe escribir su propia implementación de FileVisitor
. Necesitaría implementar 3 métodos más en esa clase:
-
preVisitDirectory()
: la lógica a ejecutar antes de ingresar a una carpeta; -
visitFileFailed()
: la lógica a ejecutar si un archivo no puede ser visitado (sin acceso, o por otras razones); -
postVisitDirectory()
: la lógica a ejecutar después de ingresar a una carpeta.
SimpleFileVisitor
. La lógica dentro del visitFile()
método es bastante simple: lea todas las líneas del archivo, verifique si contienen el contenido que necesitamos y, de ser así, imprima la ruta absoluta en la consola. La única línea que podría causarle dificultades es esta:
return FileVisitResult.CONTINUE;
En realidad, esto es muy simple. Aquí simplemente describimos lo que debe hacer el programa después de que se visita el archivo y se han realizado todas las operaciones necesarias. En nuestro caso, queremos seguir recorriendo el árbol, por lo que elegimos la CONTINUE
opción. Pero, alternativamente, podríamos tener un objetivo diferente: en lugar de encontrar todos los archivos que contengan "Este es el archivo que necesitamos", busque solo uno de esos archivos . Después de eso, el programa debería terminar. En este caso, nuestro código se vería exactamente igual, pero en lugar de romper habría:
return FileVisitResult.TERMINATE;
Bueno, ejecutemos nuestro código y veamos si funciona.
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());
}
}
Salida de la consola:
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
¡Excelente! ¡Funcionó! :) También podría aceptar este pequeño desafío: reemplácelo SimpleFileVisitor
con un ordinario FileVisitor
, anule los 4 métodos y proponga su propio propósito para el programa. Por ejemplo, podría escribir un programa que registre todas sus acciones: muestre el nombre del archivo o carpeta antes o después de ingresarlos. Eso es todo por ahora. ¡Nos vemos pronto! :)
GO TO FULL VERSION