Manipular arquivos é uma tarefa essencial em praticamente qualquer programa Python, seja para ler dados de configuração, salvar resultados de processamento ou persistir informações entre execuções. Nesta aula, você aprenderá a trabalhar com arquivos de texto e binários usando a função embutida open(), além de boas práticas como o uso de context managers e a especificação correta de encoding.

Dominar a leitura e escrita de arquivos é fundamental para construir aplicações reais, como scripts de automação, análise de dados e web scraping. Vamos explorar desde os conceitos básicos até detalhes que evitam erros comuns, garantindo que seu código seja robusto e portável.

open e modos

A função open() é a porta de entrada para qualquer operação com arquivos. Ela recebe como primeiro argumento o caminho do arquivo e, opcionalmente, um segundo argumento que especifica o modo de abertura. O modo define se você vai ler, escrever, anexar dados, trabalhar com binário ou texto, entre outros.

Os modos mais comuns são: 'r' (leitura, padrão), 'w' (escrita, sobrescreve o conteúdo existente), 'a' (anexar, adiciona ao final), 'x' (criação exclusiva, falha se o arquivo existir) e 'b' (modo binário), que pode ser combinado com os anteriores (ex.: 'rb', 'wb'). Além disso, há o modificador '+' para leitura e escrita simultâneas (ex.: 'r+').

Ao abrir um arquivo, é importante considerar o diretório atual e usar caminhos relativos ou absolutos. O Python também oferece o módulo pathlib para manipulação mais moderna, mas open() continua sendo a base.

Exemplo de abertura em diferentes modos:

# Abre para leitura (padrão)
arquivo = open('dados.txt', 'r')

# Abre para escrita (sobrescreve)
arquivo = open('dados.txt', 'w')

# Abre para anexar (adiciona ao final)
arquivo = open('dados.txt', 'a')

# Abre para criação exclusiva
arquivo = open('novo.txt', 'x')

# Abre em modo binário para leitura
arquivo = open('imagem.png', 'rb')

# Abre para leitura e escrita
arquivo = open('dados.txt', 'r+')

Lembre-se de sempre fechar o arquivo após o uso, pois deixá-lo aberto pode causar vazamento de recursos. No entanto, a maneira mais segura é usar o context manager, que veremos a seguir.

with (context manager)

O with é uma declaração que cria um contexto de execução, garantindo que o arquivo seja fechado automaticamente ao final do bloco, mesmo que ocorra uma exceção. Isso elimina a necessidade de chamar close() manualmente e torna o código mais limpo e seguro.

A sintaxe é with open(...) as variavel:. Dentro do bloco, você usa a variável para realizar operações de leitura ou escrita. Ao sair do bloco, o Python chama o método __exit__ do objeto arquivo, que fecha o arquivo. Esse padrão é amplamente recomendado pela comunidade Python.

Exemplo básico:

with open('dados.txt', 'r') as arquivo:
    conteudo = arquivo.read()
    print(conteudo)
# O arquivo é fechado automaticamente aqui

Além da segurança, o with também funciona com outros recursos, como conexões de rede e locks, mas aqui focamos em arquivos. É uma boa prática usar with sempre que possível.

Lendo e escrevendo

Python oferece vários métodos para ler e escrever conteúdo em arquivos. Para leitura, temos read() (lê todo o conteúdo como string), readline() (lê uma linha por vez) e readlines() (lê todas as linhas e retorna uma lista). Para escrita, usamos write() para escrever uma string e writelines() para escrever uma lista de strings.

É importante notar que write() não adiciona quebras de linha automaticamente; você deve incluir '\n' quando necessário. Além disso, ao trabalhar com arquivos grandes, é mais eficiente ler linha por linha do que carregar tudo na memória.

Exemplo de leitura completa:

with open('dados.txt', 'r') as f:
    conteudo = f.read()
    print(conteudo)

Exemplo de leitura linha por linha:

with open('dados.txt', 'r') as f:
    for linha in f:
        print(linha.strip())  # strip remove a quebra de linha

Exemplo de escrita:

with open('saida.txt', 'w') as f:
    f.write('Linha 1\n')
    f.write('Linha 2\n')
    f.writelines(['Linha 3\n', 'Linha 4\n'])

Para anexar, use o modo 'a':

with open('saida.txt', 'a') as f:
    f.write('Mais uma linha\n')

Lembre-se de que, ao escrever, o arquivo é criado se não existir (nos modos 'w' e 'a'), e no modo 'w' o conteúdo anterior é truncado.

Encoding

Encoding (codificação) define como os caracteres são representados em bytes. Em Python 3, as strings são Unicode, mas ao ler ou escrever em arquivos, precisamos especificar a codificação para que os bytes sejam interpretados corretamente. O padrão é UTF-8, que é amplamente usado e suporta todos os caracteres Unicode.

Se você não especificar o encoding, o Python usa o padrão da plataforma, que pode variar (ex.: UTF-8 no Linux, cp1252 no Windows). Isso pode causar erros ou corromper dados ao ler arquivos com caracteres especiais. Portanto, é sempre recomendado passar o parâmetro encoding para open().

Exemplo de leitura com encoding explícito:

with open('dados.txt', 'r', encoding='utf-8') as f:
    conteudo = f.read()
    print(conteudo)

Exemplo de escrita com encoding:

with open('saida.txt', 'w', encoding='utf-8') as f:
    f.write('Olá, mundo!\n')

Ao lidar com arquivos que não estão em UTF-8, você pode especificar outros encodings, como 'latin-1' ou 'cp1252'. Se encontrar um erro de UnicodeDecodeError, significa que o encoding especificado não corresponde ao conteúdo do arquivo. Nesse caso, tente outros encodings ou use o parâmetro errors para lidar com erros (ex.: errors='ignore').

Boas práticas: sempre use UTF-8 por padrão, pois é o mais universal e recomendado para novos projetos.

Boas práticas e observações finais

Ao trabalhar com arquivos, siga estas recomendações para evitar problemas:

  • Sempre use with para garantir o fechamento automático.
  • Especifique o encoding explicitamente, especialmente se o arquivo contiver caracteres não ASCII.
  • Prefira ler linha por linha em arquivos grandes para economizar memória.
  • Use pathlib.Path para manipular caminhos de forma mais intuitiva e multiplataforma.
  • Trate exceções como FileNotFoundError e PermissionError para tornar o programa robusto.

Com esses conhecimentos, você está pronto para integrar a manipulação de arquivos em seus projetos Python de forma eficiente e segura.

Referências

Exercícios

  1. Crie um arquivo chamado 'exercicio1.txt' e escreva nele as três linhas: 'Python', 'é', 'incrível'. Use o modo 'w' e depois leia e imprima o conteúdo.

    ✓ Resposta:
    with open('exercicio1.txt', 'w', encoding='utf-8') as f:
        f.write('Python\n')
        f.write('é\n')
        f.write('incrível\n')
    
    with open('exercicio1.txt', 'r', encoding='utf-8') as f:
        print(f.read())
    
  2. Escreva um programa que leia um arquivo 'dados.txt' e conte quantas linhas ele possui. Use o modo 'r' e o loop for.

    ✓ Resposta:
    contador = 0
    with open('dados.txt', 'r', encoding='utf-8') as f:
        for linha in f:
            contador += 1
    print(f'Número de linhas: {contador}')
    
  3. Dada uma lista de nomes nomes = ['Ana', 'João', 'Maria'], escreva cada nome em uma linha de um arquivo 'nomes.txt' (modo 'w'). Depois, abra o arquivo e exiba os nomes em ordem alfabética.

    ✓ Resposta:
    nomes = ['Ana', 'João', 'Maria']
    with open('nomes.txt', 'w', encoding='utf-8') as f:
        for nome in nomes:
            f.write(nome + '\n')
    
    with open('nomes.txt', 'r', encoding='utf-8') as f:
        linhas = [linha.strip() for linha in f]
        linhas.sort()
        for nome in linhas:
            print(nome)
    
  4. O que acontece se você tentar abrir um arquivo inexistente no modo 'r'? Escreva um código que trate essa exceção usando try/except e exiba uma mensagem amigável.

    ✓ Resposta:
    try:
        with open('nao_existe.txt', 'r', encoding='utf-8') as f:
            conteudo = f.read()
    except FileNotFoundError:
        print('O arquivo não foi encontrado.')
    
  5. Crie um programa que copie o conteúdo de um arquivo 'origem.txt' para 'destino.txt'. Use o modo 'r' para ler e 'w' para escrever. Garanta que o encoding seja UTF-8 em ambos.

    ✓ Resposta:
    with open('origem.txt', 'r', encoding='utf-8') as origem:
        conteudo = origem.read()
    
    with open('destino.txt', 'w', encoding='utf-8') as destino:
        destino.write(conteudo)