Como ler um arquivo em uma string Java: análise de uma pergunta popular do Stack Overflow
\n\nSe você já se perguntou "How do I create a Java string from the contents of a file?" — você não está sozinho. Esta pergunta acumulou milhares de visualizações no Stack Overflow, e não é à toa. Uma operação aparentemente simples — ler um arquivo de texto e colocar seu conteúdo em uma string — esconde várias armadilhas: desde vazamentos de memória até problemas de codificação.
\n\nNeste artigo, não vamos apenas dar receitas prontas. Vamos analisar por baixo dos panos de cada método: como funciona o BufferedReader, por que o Files.readString() só apareceu no Java 11, e como o Scanner difere do InputStream. Vamos lá!
Por que isso é mais complicado do que parece?
\n\nÀ primeira vista, a tarefa é trivial: existe um arquivo, existe uma string. Mas Java é uma linguagem com tipagem forte e gerenciamento de memória. Para ler um arquivo, é necessário:
\n\n- \n
- Abrir um fluxo de dados (InputStream, Reader) \n
- Bufferizar a leitura (para não acessar o disco a cada byte) \n
- Reunir os bytes/caracteres em um buffer único \n
- Converter para string considerando a codificação \n
- Fechar os recursos (para evitar vazamento de descritores de arquivo) \n
Cada um desses passos é um ponto potencial de falha. Vamos analisar as abordagens mais populares.
\n\nMétodo 1: Clássico — BufferedReader (Java 1.1+)
\n\nO método mais antigo e testado. Usa BufferedReader para leitura linha por linha. Internamente, um buffer (padrão de 8 KB) acumula dados, reduzindo o número de chamadas de sistema read().
public static String readFileBufferedReader(String filePath) throws IOException {\n StringBuilder content = new StringBuilder();\n try (BufferedReader reader = new BufferedReader(new FileReader(filePath, StandardCharsets.UTF_8))) {\n String line;\n while ((line = reader.readLine()) != null) {\n content.append(line).append(System.lineSeparator());\n }\n }\n return content.toString();\n}\n\nComo funciona por baixo dos panos?
\nFileReader abre o arquivo e cria um FileInputStream. BufferedReader o envolve, adicionando um buffer. Ao chamar readLine(), um bloco de dados (por exemplo, 8 KB) é lido, e a linha é montada a partir do buffer. Se a linha for maior que o buffer, o buffer é relido. O try-with-resources garante o fechamento do fluxo mesmo em caso de exceção.
Vantagens: Funciona em qualquer versão do Java, leitura linha por linha controlada.
\nDesvantagens: Adiciona um separador de linha extra no final, ineficiente para arquivos enormes (string na memória).
Método 2: Scanner — prático, mas lento (Java 5+)
\n\nMuitos gostam do Scanner pela simplicidade. Ele consegue dividir a entrada em tokens, mas para ler o arquivo inteiro é usado assim:
public static String readFileScanner(String filePath) throws IOException {\n try (Scanner scanner = new Scanner(new File(filePath), StandardCharsets.UTF_8.name())) {\n scanner.useDelimiter("\\\\A"); // Início do arquivo como delimitador\n return scanner.hasNext() ? scanner.next() : "";\n }\n}\n\nMagia do \\\\A: É uma expressão regular que denota o início dos dados de entrada. Ao defini-la como delimitador, forçamos o Scanner a ler todo o conteúdo de uma só vez. Internamente, o Scanner usa Pattern para encontrar o delimitador, o que adiciona custos extras com expressões regulares.
Vantagens: Código muito conciso.
\nDesvantagens: Mais lento que o BufferedReader devido ao uso de regex, não adequado para arquivos binários.
Método 3: Files.readAllLines() — praticidade do Java 8 (NIO)
\n\nA partir do Java 8, o pacote java.nio.file ofereceu uma maneira elegante