Java - Gravar no arquivo

Java - Gravar no arquivo

1. Visão geral

Neste tutorial, exploraremos diferentes maneiras dewrite to a file using Java. UsaremosBufferedWriter,PrintWriter,FileOutputStream,DataOutputStream,RandomAccessFile,FileChannele o Java 7Files classe de utilidade.

Também veremos como bloquear o arquivo durante a gravação e discutiremos algumas dicas finais sobre como gravar no arquivo.

Este artigo faz parte dethe “Java – Back to Basic” series aqui no exemplo.

Leitura adicional:

Java - Anexar dados a um arquivo

Um guia rápido e prático para anexar dados a arquivos.

Read more

FileNotFoundException em Java

Um guia rápido e prático para FileNotFoundException em Java.

Read more

Como copiar um arquivo com Java

Veja algumas maneiras comuns de copiar arquivos em Java.

Read more

2. Escreva comBufferedWriter

Vamos começar simples - e usarBufferedWriter para escrever umStringto a new file:

public void whenWriteStringUsingBufferedWritter_thenCorrect()
  throws IOException {
    String str = "Hello";
    BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
    writer.write(str);

    writer.close();
}

A saída no arquivo será:

Hello

Podemos entãoappend a String to the existing file:

@Test
public void whenAppendStringUsingBufferedWritter_thenOldContentShouldExistToo()
  throws IOException {
    String str = "World";
    BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, true));
    writer.append(' ');
    writer.append(str);

    writer.close();
}

O arquivo será então:

Hello World

3. Escreva comPrintWriter

A seguir - vamos ver como podemos usar umPrintWriter para gravar texto formatado em um arquivo:

@Test
public void givenWritingStringToFile_whenUsingPrintWriter_thenCorrect()
  throws IOException {
    FileWriter fileWriter = new FileWriter(fileName);
    PrintWriter printWriter = new PrintWriter(fileWriter);
    printWriter.print("Some String");
    printWriter.printf("Product name is %s and its price is %d $", "iPhone", 1000);
    printWriter.close();
}

O arquivo resultante conterá:

Some String
Product name is iPhone and its price is 1000$

Observe como não estamos apenas escrevendo uma string bruta no arquivo, mas também algum texto formatado com o métodoprintf.

Podemos criar o gravador usandoFileWriter,BufferedWriter ou mesmoSystem.out.

4. Escreva comFileOutputStream

Vamos agora ver como podemos usarFileOutputStream awrite binary data to a file. O código a seguir converte aString bytes internos e grava os bytes no arquivo usando umFileOutputStream:

@Test
public void givenWritingStringToFile_whenUsingFileOutputStream_thenCorrect()
  throws IOException {
    String str = "Hello";
    FileOutputStream outputStream = new FileOutputStream(fileName);
    byte[] strToBytes = str.getBytes();
    outputStream.write(strToBytes);

    outputStream.close();
}

A saída no arquivo será obviamente:

Hello

5. Escreva comDataOutputStream

A seguir - vamos dar uma olhada em como podemos usar umDataOutputStream para gravar uma String no arquivo:

@Test
public void givenWritingToFile_whenUsingDataOutputStream_thenCorrect()
  throws IOException {
    String value = "Hello";
    FileOutputStream fos = new FileOutputStream(fileName);
    DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(fos));
    outStream.writeUTF(value);
    outStream.close();

    // verify the results
    String result;
    FileInputStream fis = new FileInputStream(fileName);
    DataInputStream reader = new DataInputStream(fis);
    result = reader.readUTF();
    reader.close();

    assertEquals(value, result);
}

6. Escreva comRandomAccessFile

Vamos agora ilustrar como escrever e editar dentro de um arquivo existente - em vez de apenas gravar em um arquivo completamente novo ou anexar a um existente. Simplificando - precisamos de acesso aleatório.

RandomAccessFile nos permite escrever em uma posição específica no arquivo dado o deslocamento - desde o início do arquivo - em bytes. O código a seguir grava um valor inteiro com o deslocamento fornecido desde o início do arquivo:

private void writeToPosition(String filename, int data, long position)
  throws IOException {
    RandomAccessFile writer = new RandomAccessFile(filename, "rw");
    writer.seek(position);
    writer.writeInt(data);
    writer.close();
}

Se quisermos ler o int armazenado em um local específico, podemos usar o seguinte método:

private int readFromPosition(String filename, long position)
  throws IOException {
    int result = 0;
    RandomAccessFile reader = new RandomAccessFile(filename, "r");
    reader.seek(position);
    result = reader.readInt();
    reader.close();
    return result;
}

Para testar nossas funções, vamos escrever um inteiro - editá-lo - e, finalmente, lê-lo de volta:

@Test
public void whenWritingToSpecificPositionInFile_thenCorrect()
  throws IOException {
    int data1 = 2014;
    int data2 = 1500;

    writeToPosition(fileName, data1, 4);
    assertEquals(data1, readFromPosition(fileName, 4));

    writeToPosition(fileName2, data2, 4);
    assertEquals(data2, readFromPosition(fileName, 4));
}

7. Escreva comFileChannel

Se você estiver lidando com arquivos grandes,FileChannel pode ser mais rápido que o IO padrão. O código a seguir gravaString em um arquivo usandoFileChannel:

@Test
public void givenWritingToFile_whenUsingFileChannel_thenCorrect()
  throws IOException {
    RandomAccessFile stream = new RandomAccessFile(fileName, "rw");
    FileChannel channel = stream.getChannel();
    String value = "Hello";
    byte[] strBytes = value.getBytes();
    ByteBuffer buffer = ByteBuffer.allocate(strBytes.length);
    buffer.put(strBytes);
    buffer.flip();
    channel.write(buffer);
    stream.close();
    channel.close();

    // verify
    RandomAccessFile reader = new RandomAccessFile(fileName, "r");
    assertEquals(value, reader.readLine());
    reader.close();
}

8. Escreva com a classeFiles

Java 7 apresenta uma nova maneira de trabalhar com o sistema de arquivos, junto com uma nova classe de utilitário -Files. Usando a classeFiles, podemos criar, mover, copiar, excluir arquivos e diretórios também; também pode ser usado para ler e gravar em um arquivo:

@Test
public void givenUsingJava7_whenWritingToFile_thenCorrect()
  throws IOException {
    String str = "Hello";

    Path path = Paths.get(fileName);
    byte[] strToBytes = str.getBytes();

    Files.write(path, strToBytes);

    String read = Files.readAllLines(path).get(0);
    assertEquals(str, read);
}

9. Gravar em um arquivo temporário

Agora, vamos tentar gravar em um arquivo temporário. O código a seguir cria um arquivo temporário e grava uma String nele:

@Test
public void whenWriteToTmpFile_thenCorrect() throws IOException {
    String toWrite = "Hello";
    File tmpFile = File.createTempFile("test", ".tmp");
    FileWriter writer = new FileWriter(tmpFile);
    writer.write(toWrite);
    writer.close();

    BufferedReader reader = new BufferedReader(new FileReader(tmpFile));
    assertEquals(toWrite, reader.readLine());
    reader.close();
}

Então, como você pode ver - é apenas a criação do arquivo temporário que é interessante e diferente - depois desse ponto, gravar no arquivo é o mesmo.

10. Bloquear arquivo antes de gravar

Finalmente, ao gravar em um arquivo, às vezes você precisa ter certeza de que mais ninguém está gravando nesse arquivo ao mesmo tempo. Basicamente - você precisa bloquear esse arquivo enquanto escreve.

Vamos usarFileChannel para tentar bloquear o arquivo antes de gravá-lo:

@Test
public void whenTryToLockFile_thenItShouldBeLocked()
  throws IOException {
    RandomAccessFile stream = new RandomAccessFile(fileName, "rw");
    FileChannel channel = stream.getChannel();

    FileLock lock = null;
    try {
        lock = channel.tryLock();
    } catch (final OverlappingFileLockException e) {
        stream.close();
        channel.close();
    }
    stream.writeChars("test lock");
    lock.release();

    stream.close();
    channel.close();
}

Observe que se o arquivo já estiver bloqueado quando tentarmos obter o bloqueio, umOverlappingFileLockException será lançado.

11. Notas

Depois de explorar tantos métodos de gravação em um arquivo, vamos discutir algumas notas importantes:

  • Se tentarmos ler de um arquivo que não existe, umFileNotFoundException será lançado

  • Se tentarmos gravar em um arquivo que não existe, o arquivo será criado primeiro e nenhuma exceção será lançada

  • É muito importante fechar o fluxo depois de usá-lo, pois não está fechado implicitamente, para liberar quaisquer recursos associados a ele

  • No fluxo de saída, o métodoclose() chamaflush() antes de liberar os recursos, o que força todos os bytes armazenados em buffer a serem gravados no fluxo

Olhando para as práticas de uso comuns, podemos ver - por exemplo - quePrintWriter é usado para escrever texto formatado; FileOutputStream para escrever dados binários; DataOutputStream para escrever tipos de dados primitivos; RandomAccessFile para escrever em uma posição específica; FileChannel para escrever mais rápido em arquivos maiores. Algumas das APIs dessas classes permitem mais, mas esse é um bom ponto de partida.

12. Conclusão

Este artigo ilustra as muitas opções de gravação de dados em um arquivo usando Java.

A implementação de todos esses exemplos e trechos de códigocan be found over on GitHub project - este é um projeto baseado em Maven, portanto, deve ser fácil de importar e executar como está.