File
. Você pode ler sobre seu trabalho aqui . Mas no Java 7, os criadores da linguagem decidiram mudar a forma como trabalham com arquivos e diretórios. Isso se deveu ao fato de a classe File
apresentar uma série de desvantagens. Por exemplo, ele não tinha um método copy()
que permitisse copiar um arquivo de um local para outro (um recurso aparentemente necessário). Além disso, a classe File
tinha vários métodos que retornavam boolean
valores. Se ocorrer um erro, esse método retornará falso em vez de lançar uma exceção, o que torna muito difícil diagnosticar erros e determinar suas causas. Em vez de uma única classe, File
apareceram até 3 classes Paths
: Path
e Files
. Bem, para ser mais preciso, Path
esta é uma interface, não uma classe. Vamos descobrir como eles diferem entre si e por que cada um deles é necessário. Vamos começar com a coisa mais fácil - Paths
.
Caminhos
Paths
é uma classe muito simples com um único método estático get()
. Ele foi criado exclusivamente para obter um objeto do tipo da string ou URI passada Path
. Não possui outra funcionalidade. Aqui está um exemplo de seu trabalho:
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");
}
}
Não é a aula mais difícil, certo? :) Bem, já que temos um objeto do tipo Path
, vamos descobrir o que é Path
e por que é necessário :)
Caminho
Path
, em geral, é um análogo redesenhado do File
. É muito mais fácil trabalhar com isso do que com File
. Primeiramente , muitos métodos utilitários (estáticos) foram removidos dele e movidos para a classe Files
. Em segundo lugar , Path
os valores de retorno dos métodos foram ordenados. Na aula, File
os métodos retornavam this String
, that boolean
, that File
- não foi fácil descobrir. Por exemplo, havia um método getParent()
que retornava o caminho pai do arquivo atual como uma string. Mas ao mesmo tempo existia um método getParentFile()
que retornava a mesma coisa, mas na forma de um objeto File
! Isto é claramente redundante. Portanto, na interface, Path
o método getParent()
e outros métodos para trabalhar com arquivos simplesmente retornam um objeto Path
. Não há muitas opções - tudo é fácil e simples. Que métodos úteis ele possui Path
? Aqui estão alguns deles e exemplos de seu trabalho:
-
getFileName()
— retorna o nome do arquivo do caminho; -
getParent()
— retorna o diretório “pai” em relação ao caminho atual (ou seja, o diretório que está mais acima na árvore de diretórios); -
getRoot()
— retorna o diretório “raiz”; isto é, aquele que está no topo da árvore de diretórios; -
startsWith()
,endsWith()
— verifica se o caminho começa/termina com o caminho passado: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); } }
Saída do console:
testFile.txt
C:\Usuários\Nome de usuário\Desktop
C:\
verdadeiro
falsoPreste atenção em como o método funciona
endsWith()
. Ele verifica se o caminho atual termina com o caminho passado . Está no caminho e não no conjunto de caracteres .Compare os resultados dessas duas chamadas:
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")); } }
Saída do console:
falso
verdadeiroVocê precisa passar o caminho completo para o método
endsWith()
, e não apenas um conjunto de caracteres: caso contrário, o resultado sempre será false , mesmo que o caminho atual realmente termine com tal sequência de caracteres (como no caso de “estFile.txt ”No exemplo acima).Além disso, existe
Path
um grupo de métodos que simplifica o trabalho com caminhos absolutos (completos) e relativos .
-
boolean isAbsolute()
— retorna verdadeiro se o caminho atual for absoluto: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()); } }
Saída do console:
verdadeiro
-
Path normalize()
— “normaliza” o caminho atual, removendo dele elementos desnecessários. Você deve saber que os sistemas operacionais populares costumam usar caracteres “.” ao denotar caminhos. (“diretório atual”) e “..” (diretório pai). Por exemplo: “ ./Pictures/dog.jpg ” significa que no diretório em que estamos agora, existe uma pasta Pictures, e nela existe um arquivo “dog.jpg”Então aqui está. Se o seu programa tiver um caminho que usa “.” ou “..”, o método
normalize()
irá removê-los e obter um caminho que não os conterá: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()); } }
Saída do console:
C:\Usuários\Java\exemplos
C:\Usuários\exemplos -
Path relativize()
— calcula o caminho relativo entre o caminho atual e o passado.Por exemplo:
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)); } }
Saída do console:
Nome de usuário\Desktop\testFile.txt
Path
é bastante grande. Você pode encontrar todos eles na documentação da Oracle . Passaremos à revisão Files
.
arquivos
Files
- esta é uma classe utilitária, onde os métodos estáticos da classe foram movidos File
. Files
- é aproximadamente igual a Arrays
ou Collections
, só que funciona com arquivos, e não com arrays e coleções :) É focado no gerenciamento de arquivos e diretórios. Usando métodos estáticos Files
, podemos criar, excluir e mover arquivos e diretórios. Para essas operações são usados os métodos createFile()
(para diretórios - createDirectory()
) move()
e delete()
. Veja como usá-los:
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")));
}
}
Aqui, primeiro criamos um arquivo (método Files.createFile()
) na área de trabalho e, em seguida, criamos uma pasta lá (método Files.createDirectory()
). Depois disso, movemos o arquivo (método Files.move()
) da área de trabalho para esta nova pasta, e ao final excluímos o arquivo (método Files.delete()
). Saída do console: o arquivo foi criado com sucesso? true O diretório foi criado com sucesso? true Nosso arquivo ainda está na área de trabalho? false Nosso arquivo foi movido para testDirectory? true O arquivo ainda existe? falso Prestar atenção:Assim como os métodos de interface Path
, muitos métodos Files
retornam um objetoPath
. A maioria dos métodos de classe Files
também aceita Path
. Aqui o método se tornará seu fiel assistente Paths.get()
- use-o ativamente. O que mais é interessante Files
? O que realmente faltava na classe antiga era o File
método copy()
! . Falamos sobre ele no início da palestra, agora é a hora de conhecê-lo!
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")));
}
}
Saída do console: o arquivo foi criado com sucesso? true O diretório foi criado com sucesso? true Nosso arquivo ainda está na área de trabalho? true Nosso arquivo foi copiado para testDirectory? true Agora você pode copiar arquivos programaticamente! :) Mas a aula Files
permite não só gerenciar os próprios arquivos, mas também trabalhar com seu conteúdo. Para gravar dados em um arquivo, ele possui um método write()
, e para leitura - até 3:, read()
e readAllBytes()
nos readAllLines()
deteremos neste último em detalhes. Por que nisso? Porque tem um tipo de retorno muito interessante - List<String>
! Ou seja, ele nos retorna uma lista de linhas do arquivo. Claro, isso torna o trabalho com o conteúdo muito conveniente, porque o arquivo inteiro, linha por linha, pode, por exemplo, ser enviado para o console em um loop regular 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);
}
}
}
Saída do console: Lembro-me de um momento maravilhoso: Você apareceu diante de mim, Como uma visão fugaz, Como um gênio de pura beleza. Muito confortavelmente! :) Esse recurso apareceu no Java 7. No Java 8 apareceu a API Stream , que adicionou alguns elementos de programação funcional ao Java. Incluindo recursos mais avançados para trabalhar com arquivos. Imagine que temos uma tarefa: encontrar todas as linhas em um arquivo que comecem com a palavra “Como”, convertê-las para MAIÚSCULAS e enviá-las para o console. Como seria uma solução usando uma classe Files
em Java 7? Algo assim:
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);
}
}
}
Saída do console: COMO UMA VISÃO RÁPIDA, COMO UM GÊNIO DE PURA BELEZA. Parece que conseguimos, mas você não acha que para uma tarefa tão simples nosso código acabou sendo um pouco... detalhado? Usando a API Java 8 Stream a solução parece muito mais 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);
}
}
Conseguimos o mesmo resultado, mas com muito menos código! Além disso, não se pode dizer que perdemos em “legibilidade”. Acho que você pode comentar facilmente o que esse código faz, mesmo se não estiver familiarizado com a API Stream. Mas, resumindo, um Stream é uma sequência de elementos nos quais você pode executar diferentes funções. Obtemos o objeto Stream do método Files.lines()
e aplicamos 3 funções a ele:
-
Usando o método,
filter()
selecionamos apenas as linhas do arquivo que começam com “Como”. -
Percorremos todas as linhas selecionadas usando o método
map()
e trazemos cada uma delas para MAIÚSCULAS. -
Combinamos todas as linhas resultantes
List
usando o arquivocollect()
.
Files.walkFileTree()
. Aqui está o que precisamos fazer. Primeiro, precisamos FileVisitor
. FileVisitor
é uma interface especial que descreve todos os métodos para percorrer a árvore de arquivos. Especificamente, colocaremos lógica para ler o conteúdo do arquivo e verificar se ele contém o texto que precisamos. Assim será o nosso 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;
}
}
Neste caso, nossa classe herda de SimpleFileVisitor
. Esta é uma classe que implementa FileVisitor
, na qual você precisa substituir apenas um método: visitFile()
. Aqui descrevemos o que precisa ser feito com cada arquivo em cada diretório. Se precisar de uma lógica de travessia mais complexa, você deve escrever sua própria implementação FileVisitor
. Lá você precisará implementar mais 3 métodos:
-
preVisitDirectory()
— lógica que deve ser executada antes de entrar na pasta; -
visitFileFailed()
— o que fazer se for impossível inserir o arquivo (falta de acesso ou outros motivos); -
postVisitDirectory()
— a lógica que deve ser executada após entrar na pasta.
SimpleFileVisitor
. A lógica dentro do método visitFile()
é bastante simples: leia todas as linhas do arquivo, verifique se elas contêm o conteúdo que precisamos e, em caso afirmativo, imprima o caminho absoluto para o console. A única linha que pode lhe causar problemas é esta:
return FileVisitResult.CONTINUE;
Na verdade, tudo é simples. Aqui descrevemos simplesmente o que o programa deve fazer depois que o arquivo for inserido e todas as operações necessárias forem concluídas. No nosso caso, precisamos continuar percorrendo a árvore, então escolhemos a opção CONTINUE
. Mas nós, por exemplo, poderíamos ter outra tarefa: encontrar não todos os arquivos que contenham “Este é o arquivo que precisamos”, mas apenas um desses arquivos . Depois disso, o programa deve ser encerrado. Nesse caso, nosso código seria exatamente igual, mas em vez de break; seria:
return FileVisitResult.TERMINATE;
Bem, vamos executar nosso código e ver se 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());
}
}
Saída do console: O arquivo necessário foi encontrado! C:\Users\Username\Desktop\testFolder\FileWeNeed1.txt O arquivo necessário foi encontrado! C:\Users\Username\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt O arquivo necessário foi encontrado! C:\Users\Username\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt Ótimo, conseguimos! :) Se você quiser saber mais sobre walkFileTree()
, recomendo este artigo . Você também pode concluir uma pequena tarefa - substituí-la SimpleFileVisitor
por uma normal FileVisitor
, implementar todos os 4 métodos e definir uma finalidade para este programa. Por exemplo, você pode escrever um programa que registre todas as suas ações: exiba o nome de um arquivo ou pasta no console antes/depois de inseri-las. Isso é tudo - até mais! :)
GO TO FULL VERSION