JavaRush /Blogue Java /Random-PT /Arquivos Java, caminho

Arquivos Java, caminho

Publicado no grupo Random-PT
Olá! Hoje falaremos sobre como trabalhar com arquivos e diretórios. Você já sabe como gerenciar o conteúdo dos arquivos: tivemos muitas aulas dedicadas a isso :) Acho que você pode facilmente se lembrar de várias aulas que são necessárias para esses fins. Na palestra de hoje falaremos especificamente sobre gerenciamento de arquivos – criação, renomeação, etc. Antes do Java 7, todas essas operações eram executadas usando o 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 Fileapresentar 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 Filetinha vários métodos que retornavam booleanvalores. 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, Fileapareceram até 3 classes Paths: Pathe Files. Bem, para ser mais preciso, Pathesta é 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 é Pathe 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 , Pathos valores de retorno dos métodos foram ordenados. Na aula, Fileos 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, Patho 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
    falso

    Preste 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
    verdadeiro

    Você 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 Pathum grupo de métodos que simplifica o trabalho com caminhos absolutos (completos) e relativos .

Vejamos estes métodos:
  • 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

A lista completa de métodos 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 Arraysou 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 Filesretornam um objetoPath . A maioria dos métodos de classe Filestambé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 Filemé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 Filespermite 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 Filesem 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:
  1. Usando o método, filter()selecionamos apenas as linhas do arquivo que começam com “Como”.

  2. Percorremos todas as linhas selecionadas usando o método map()e trazemos cada uma delas para MAIÚSCULAS.

  3. Combinamos todas as linhas resultantes Listusando o arquivo collect().

Na saída obtemos o mesmo resultado: COMO UMA VISÃO DE JEJUM, COMO UM GÊNIO DE PURA BELEZA. Se você estiver interessado em aprender mais sobre os recursos desta biblioteca, recomendamos a leitura deste artigo . Voltaremos às nossas ovelhas, ou seja, aos arquivos :) A última possibilidade que consideraremos hoje é percorrer a árvore de arquivos . A estrutura de arquivos nos sistemas operacionais modernos geralmente assume a forma de uma árvore: tem uma raiz e há ramificações das quais outras ramificações podem ser separadas, etc. Os diretórios desempenham o papel de raiz e ramificações. Por exemplo, o diretório “ C:// ” pode atuar como raiz . Dois ramos se ramificam dele: “ C://Downloads ” e “ C://Users ”. De cada um desses ramos existem mais 2 ramos: “ C://Downloads/Pictures ”, “ C://Downloads/Video ”, “ C://Users/JohnSmith ”, “ C://Users/Pudge2005 ” . Outros ramos se ramificam desses ramos, etc. - é assim que fica uma árvore. No Linux parece a mesma coisa, só que aí o diretório atua como raiz / Arquivos, Caminho - 2 Agora imagine que temos uma tarefa: conhecendo o diretório raiz, devemos percorrê-lo, procurar pastas de todos os níveis e encontrar nelas arquivos com o conteúdo nós precisamos. Procuraremos arquivos que contenham a linha “Este é o arquivo que precisamos!” Nosso diretório raiz será a pasta “testFolder”, localizada na área de trabalho. Dentro dela possui o seguinte conteúdo: Arquivos, Caminho - 3Dentro das pastas nível1-a e nível1-b também existem pastas: Arquivos, Caminho - 4Arquivos, Caminho - 5Dentro dessas “pastas de segundo nível” não existem mais pastas, apenas arquivos individuais: Arquivos, Caminho - 6Arquivos, Caminho - 7Designaremos especialmente 3 arquivos com o conteúdo que desejamos. precisamos com nomes claros - FileWeNeed1.txt, FileWeNeed2.txt, FileWeNeed3.txt Esses são os que precisamos encontrar por conteúdo usando Java. Como podemos fazer isso? Um método muito poderoso para percorrer uma árvore de arquivos vem em socorro - 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.

Não temos essa lógica, então é o suficiente para nós 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 SimpleFileVisitorpor 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! :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION