Python Escrevendo Arquivos

Tudo o que um programa guarda em variáveis some quando ele termina. Para a lista de compras, as vendas do dia ou a tabela de preços da padaria sobreviverem até amanhã, o programa precisa escrever num arquivo. Você já sabe abrir um arquivo com open e with (aula Abrindo Arquivos) e ler o que tem nele (aula Lendo Arquivos). Nesta aula, o caminho contrário: gravar. E o ponto mais importante é um aviso: um dos modos de escrever apaga o que havia, sem perguntar.

Você vai aprender
  • Os três modos de escrita: "w" (apaga e escreve), "a" (acrescenta no fim) e "x" (só cria arquivo novo).
  • A escrever com write, e por que ele não põe a quebra de linha sozinho.
  • A escrever uma lista de uma vez com writelines.
  • A usar o print que você já conhece para escrever num arquivo: print(..., file=arquivo).
  • Por que sempre encoding="utf-8".
  • Os erros reais: FileExistsError, TypeError, not writable, a pasta que não existe e o arquivo já fechado.

A ideia: o caderno, a borracha e a página nova

A Ana anota a lista de compras num caderno. Ela tem três jeitos de escrever nele:

No cadernoModoO que acontece com o arquivo
Apagar a página inteira com a borracha e escrever de novo"w" (write, "escrever")Se existe, o conteúdo é apagado. Se não existe, é criado
Continuar escrevendo depois da última linha"a" (append, "acrescentar")O que havia fica; o novo vai para o fim. Se não existe, é criado
Só escrever numa página em branco; se já tiver algo, não mexe"x" (exclusive, "exclusivo")Se não existe, é criado. Se já existe, dá erro e nada é tocado

O modo é o segundo argumento do open. Sem ele, o modo é "r" (read, "ler"), que só lê.


1. Modo "w" e write

O primeiro programa grava a lista de compras da padaria:

with open("compras.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("farinha")
    arquivo.write("ovos")
    arquivo.write("leite")
open("compras.txt", "w", encoding="utf-8")
Abre (ou cria) o arquivo compras.txt para escrever, em UTF-8 (a seção 5 explica o porquê).
as arquivo
O arquivo aberto fica na variável arquivo. O with fecha o arquivo sozinho no fim do bloco.
arquivo.write("farinha")
"Escreva": grava o texto no arquivo, exatamente como está.

O programa roda calado (não tem print). Para ver o que ficou no arquivo, o cat do terminal mostra o conteúdo (o ; echo só pula uma linha no fim, já você vai ver por quê):

$ python3 lista.py
$ cat compras.txt; echo
farinhaovosleite

Tudo grudado! O write grava só o que você mandou, sem espaço, sem pular linha. Diferente do print, que pula a linha sozinho no fim, o write não acrescenta nada. Quem quer uma linha por item precisa escrever a quebra de linha, o \n (aula Caracteres de Escape):

with open("compras.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("farinha\n")
    arquivo.write("ovos\n")
    arquivo.write("leite\n")
print("Lista gravada.")
$ python3 lista2.py
Lista gravada.
$ cat compras.txt
farinha
ovos
leite

Agora sim: uma linha por item. O costume é terminar toda linha com \n, inclusive a última; os programas do terminal (e o próprio Python, ao ler) esperam isso. O cat -A mostra cada final de linha como um $ (aqui, no comquebra.txt da seção 6, que tem as mesmas três linhas):

$ cat -A comquebra.txt
farinha$
ovos$
leite$

O write devolve um número

No modo interativo dá para ver um detalhe: o write devolve quantos caracteres escreveu.

$ python3 -q
>>> arquivo = open("teste.txt", "w", encoding="utf-8")
>>> arquivo.write("farinha\n")
8
>>> arquivo.write("pão de queijo\n")
14
>>> arquivo.closed
False
>>> arquivo.close()
>>> arquivo.closed
True

"farinha\n" tem 8 caracteres (o \n conta como um). Num programa em arquivo, esse número aparece só se você der print nele, e quase ninguém usa. Aqui, sem with, o arquivo teve de ser fechado à mão com close() ("fechar"); arquivo.closed diz se ele já está fechado. Com with, você não precisa lembrar disso.

Missão: o programa grava três itens, mas eles saem grudados numa linha só. Acrescente o \n no fim de cada write para o arquivo ficar com 3 linhas. (A segunda parte do programa lê o arquivo de volta e conta as linhas.)

with open("compras.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("farinha")
    arquivo.write("ovos")
    arquivo.write("leite")

with open("compras.txt", encoding="utf-8") as arquivo:
    linhas = arquivo.readlines()
print("Linhas no arquivo:", len(linhas))
print(linhas)
with open("compras.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("farinha\n")
    arquivo.write("ovos\n")
    arquivo.write("leite\n")

with open("compras.txt", encoding="utf-8") as arquivo:
    linhas = arquivo.readlines()
print("Linhas no arquivo:", len(linhas))
print(linhas)

2. Cuidado: o "w" apaga o que havia

O compras.txt tem três itens. A Ana roda outro programa, que grava só o açúcar, também com "w":

with open("compras.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("açúcar\n")
$ python3 apaga.py
$ cat compras.txt
açúcar

A farinha, os ovos e o leite sumiram. No instante em que o open com "w" abre um arquivo que existe, ele o esvazia, antes mesmo do primeiro write. Não há aviso, não há lixeira, não há "desfazer". Use "w" quando a ideia for mesmo substituir o arquivo inteiro (um relatório refeito do zero, uma tabela de preços nova). Para guardar o que havia, use "a".


3. Modo "a": acrescentar no fim

with open("compras.txt", "a", encoding="utf-8") as arquivo:
    arquivo.write("fermento\n")
$ python3 acrescenta.py
$ python3 acrescenta.py
$ cat compras.txt
açúcar
fermento
fermento

O açúcar ficou, e cada execução acrescentou um fermento no fim. O "a" nunca apaga: é o modo dos históricos, como o registro de vendas do dia ou um arquivo de log (aula logging). Repare no lado ruim: rodou duas vezes, gravou duas vezes. O "a" não confere se a linha já estava lá.

Missão: o vendas.txt (veja a aba) já tem as vendas da manhã. O programa deveria acrescentar a venda nova, mas está apagando as antigas. Troque o modo para que, no fim, o arquivo tenha as três vendas.

with open("vendas.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("bolo de milho,2\n")

with open("vendas.txt", encoding="utf-8") as arquivo:
    print(arquivo.read())
pão de queijo,12
bolo de cenoura,3
with open("vendas.txt", "a", encoding="utf-8") as arquivo:
    arquivo.write("bolo de milho,2\n")

with open("vendas.txt", encoding="utf-8") as arquivo:
    print(arquivo.read())

4. Modo "x": só se ainda não existir

E quando o arquivo não pode ser apagado de jeito nenhum? O relatório de outubro, depois de pronto, não deve ser sobrescrito por engano. O modo "x" cria o arquivo, mas se recusa a abrir um que já existe:

with open("relatorio.txt", "x", encoding="utf-8") as arquivo:
    arquivo.write("Relatório de outubro\n")
print("Relatório criado.")
$ python3 novo.py
Relatório criado.
$ python3 novo.py
Traceback (most recent call last):
  File "/home/ana/padaria/novo.py", line 1, in <module>
    with open("relatorio.txt", "x", encoding="utf-8") as arquivo:
         ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileExistsError: [Errno 17] File exists: 'relatorio.txt'
File "/home/ana/padaria/novo.py", line 1
O erro foi na linha 1, no open (as marcas ~~~~^^^^ apontam para ele).
FileExistsError
"O arquivo já existe".
[Errno 17] File exists: 'relatorio.txt'
O código 17 é o que o próprio sistema deu para "já existe", e o nome do arquivo vem no fim.

Na primeira vez, criou. Na segunda, o erro protegeu o relatório: o programa parou antes de escrever, e o arquivo ficou como estava. É um erro bom. Com try e except FileExistsError (aula try e except), o programa pode avisar com calma em vez de mostrar o traceback; é a missão final desta aula.


5. Sempre encoding="utf-8"

Um arquivo de texto guarda bytes, e a codificação (encoding) é a regra que transforma letras em bytes. Para a ou b, quase todas as regras concordam; para ç, ã, é, não. Veja o que acontece quando um arquivo é gravado numa codificação (a cp1252, comum em programas antigos do Windows) e lido em outra:

with open("windows.txt", "w", encoding="cp1252") as arquivo:
    arquivo.write("açúcar e café\n")
with open("windows.txt", encoding="utf-8") as arquivo:
    print(arquivo.read())
$ python3 latin.py
Traceback (most recent call last):
  File "/home/ana/padaria/latin.py", line 4, in <module>
    print(arquivo.read())
          ~~~~~~~~~~~~^^
  File "<frozen codecs>", line 325, in decode
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe7 in position 1: invalid continuation byte

"O decodificador UTF-8 não consegue ler o byte 0xe7 na posição 1". A posição 1 é o ç de "açúcar": na cp1252, ele virou um byte (0xe7) que não faz sentido em UTF-8. Escrever e ler com a mesma codificação resolve, e a UTF-8 é a que funciona em todo lugar: Linux, macOS, Windows, navegadores. Por isso o guia escreve encoding="utf-8" em todo open.

E se eu não escrever o encoding?

Sem ele, o Python usa a codificação padrão do sistema. No Ubuntu da Ana, é UTF-8, e tudo funciona; no Windows, até o Python 3.14, costuma ser outra, e o mesmo programa grava acentos diferentes. A partir do Python 3.15, o padrão passa a ser UTF-8 em todo lugar (PEP 686). Escrever encoding="utf-8" deixa o programa certo em qualquer versão e em qualquer computador.


6. Uma lista de uma vez: writelines

writelines ("escrever linhas") grava todos os textos de uma lista, um depois do outro. O nome engana:

itens = ["farinha", "ovos", "leite"]
with open("semquebra.txt", "w", encoding="utf-8") as arquivo:
    arquivo.writelines(itens)
with open("comquebra.txt", "w", encoding="utf-8") as arquivo:
    arquivo.writelines(item + "\n" for item in itens)
$ python3 linhas.py
$ cat semquebra.txt; echo
farinhaovosleite
$ cat comquebra.txt
farinha
ovos
leite
arquivo.writelines(itens)
Grava os três textos grudados: apesar do nome, o writelines também não põe \n. É o mesmo que um write para cada item.
item + "\n" for item in itens
Monta cada item já com a quebra de linha, como numa list comprehension sem os colchetes (aula List Comprehension).

Outra forma, bem comum, é juntar tudo num texto só antes: arquivo.write("\n".join(itens) + "\n") (o join está na aula Métodos de Texto).


7. print(..., file=arquivo): o print que você já conhece

O print sabe escrever num arquivo: basta dizer file=. E ele faz tudo o que faz na tela: pula a linha no fim, separa os valores com espaço, converte números em texto.

precos = {"bolo de cenoura": 35.0, "pão de queijo": 2.5, "café": 4.0}
with open("precos.txt", "w", encoding="utf-8") as arquivo:
    print("Tabela de preços", file=arquivo)
    for produto, preco in precos.items():
        print(f"{produto}: R$ {preco:.2f}", file=arquivo)
print("Tabela gravada.")
$ python3 com_print.py
Tabela gravada.
$ cat precos.txt
Tabela de preços
bolo de cenoura: R$ 35.00
pão de queijo: R$ 2.50
café: R$ 4.00

Só o último print, sem file=, apareceu na tela; os outros foram para o arquivo, cada um na sua linha, sem nenhum \n escrito à mão. O :.2f deixa duas casas decimais (aula f-strings).

Missão: o recibo está saindo na tela. Faça cada linha ir para o arquivo recibo.txt: acrescente file=arquivo nos três print de dentro do with. (O programa mostra o arquivo no fim.)

pedido = {"bolo de cenoura": 2, "pão de queijo": 1}
precos = {"bolo de cenoura": 35.0, "pão de queijo": 2.5}

with open("recibo.txt", "w", encoding="utf-8") as arquivo:
    total = 0
    for produto, quantidade in pedido.items():
        subtotal = quantidade * precos[produto]
        total += subtotal
        print(f"{quantidade} x {produto}: R$ {subtotal:.2f}")
    print("-" * 20)
    print(f"Total: R$ {total:.2f}")

with open("recibo.txt", encoding="utf-8") as arquivo:
    print("No recibo.txt:")
    print(arquivo.read())
pedido = {"bolo de cenoura": 2, "pão de queijo": 1}
precos = {"bolo de cenoura": 35.0, "pão de queijo": 2.5}

with open("recibo.txt", "w", encoding="utf-8") as arquivo:
    total = 0
    for produto, quantidade in pedido.items():
        subtotal = quantidade * precos[produto]
        total += subtotal
        print(f"{quantidade} x {produto}: R$ {subtotal:.2f}", file=arquivo)
    print("-" * 20, file=arquivo)
    print(f"Total: R$ {total:.2f}", file=arquivo)

with open("recibo.txt", encoding="utf-8") as arquivo:
    print("No recibo.txt:")
    print(arquivo.read())

Formas diferentes de escrever

JeitoExemploQuando usar
writearquivo.write("ovos\n")Você controla cada caractere. Lembre do \n
writelinesarquivo.writelines(linhas)Os textos já estão numa lista, cada um já com o seu \n
write + joinarquivo.write("\n".join(itens) + "\n")Uma lista de textos sem \n, numa linha de código
print(file=)print(produto, preco, file=arquivo)Relatórios e recibos: pula linha, separa com espaço e aceita números
Módulos próprioscsv, jsonPlanilhas e dados estruturados (aulas CSV e JSON)
ModoArquivo não existeArquivo existeUse para
"w"CriaApaga e escreve do zeroRefazer o arquivo inteiro
"a"CriaAcrescenta no fimHistóricos, registros, logs
"x"CriaFileExistsError, não mexeNunca sobrescrever por engano
"r" (padrão)FileNotFoundErrorSó lêLer (aula Lendo Arquivos)

Quando dá errado

Escrever um número: TypeError
total = 42
with open("total.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write(total)
$ python3 numero.py
Traceback (most recent call last):
  File "/home/ana/padaria/numero.py", line 3, in <module>
    arquivo.write(total)
    ~~~~~~~~~~~~~^^^^^^^
TypeError: write() argument must be str, not int

"O argumento do write() tem de ser str (texto), não int (número inteiro)". O print converte sozinho; o write, não. Como consertar: converta antes, com arquivo.write(str(total) + "\n") ou arquivo.write(f"{total}\n"), ou use print(total, file=arquivo).

Escrever num arquivo aberto para ler: not writable
with open("compras.txt", encoding="utf-8") as arquivo:
    arquivo.write("manteiga\n")
$ python3 leitura.py
Traceback (most recent call last):
  File "/home/ana/padaria/leitura.py", line 2, in <module>
    arquivo.write("manteiga\n")
    ~~~~~~~~~~~~~^^^^^^^^^^^^^^
io.UnsupportedOperation: not writable

"Operação não suportada: não dá para escrever". Faltou o modo no open, e sem ele o arquivo abre só para leitura ("r"). io é o módulo do Python que cuida de arquivos, por isso o nome completo do erro é io.UnsupportedOperation. Como consertar: open("compras.txt", "a", encoding="utf-8") (ou "w", se a ideia for substituir).

A pasta não existe: FileNotFoundError ao escrever
with open("relatorios/outubro.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("Vendas de outubro\n")
$ python3 pasta.py
Traceback (most recent call last):
  File "/home/ana/padaria/pasta.py", line 1, in <module>
    with open("relatorios/outubro.txt", "w", encoding="utf-8") as arquivo:
         ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'relatorios/outubro.txt'

Estranho: "arquivo não encontrado", ao criar um arquivo? O "w" cria o arquivo, mas não cria pastas, e a pasta relatorios não existe. Como consertar: crie a pasta antes, com os.makedirs("relatorios", exist_ok=True) (aula os e sys) ou com o pathlib (aula Pastas e Caminhos).

Um modo que não existe
$ python3 modo.py
Traceback (most recent call last):
  File "/home/ana/padaria/modo.py", line 1, in <module>
    with open("compras.txt", "escrever", encoding="utf-8") as arquivo:
         ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: invalid mode: 'escrever'

"Modo inválido". O modo é uma letra, em inglês: "w", "a", "x" ou "r".

Escrever depois de fechar

Continuando a sessão do >>> da seção 1, depois do close():

>>> arquivo.write("ovos\n")
Traceback (most recent call last):
  File "<python-input-6>", line 1, in <module>
    arquivo.write("ovos\n")
    ~~~~~~~~~~~~~^^^^^^^^^^
ValueError: I/O operation on closed file.

"Operação de entrada/saída num arquivo fechado". Com with, acontece o mesmo se o write ficar fora do bloco (sem os 4 espaços): o arquivo já foi fechado quando o bloco terminou. Como consertar: deixe todos os write indentados dentro do with.

O "w" que apagou o que não devia

Não tem traceback, e por isso é o mais perigoso: você viu na seção 2. Se o arquivo guarda algo que não pode se perder, abra com "a" (para acrescentar) ou com "x" (para só criar). E guarde cópias dos arquivos importantes.


Referência rápida

CódigoO que faz
with open("x.txt", "w", encoding="utf-8") as arquivo:Abre para escrever, apagando o que havia (cria se não existir)
open("x.txt", "a", encoding="utf-8")Abre para acrescentar no fim
open("x.txt", "x", encoding="utf-8")Cria; FileExistsError se já existir
arquivo.write("texto\n")Escreve o texto, sem acrescentar nada (devolve quantos caracteres)
arquivo.writelines(lista)Escreve cada texto da lista, grudados
print(..., file=arquivo)Escreve como o print: com espaço entre os valores e \n no fim
arquivo.close() / arquivo.closedFecha o arquivo / diz se está fechado (com with, é automático)
MensagemCausa provável
FileExistsError: [Errno 17] File existsModo "x" e o arquivo já existe
TypeError: write() argument must be str, not intwrite de um número: converta com str() ou f-string
io.UnsupportedOperation: not writableFaltou o modo "w"/"a"/"x" no open
FileNotFoundError ao escreverA pasta do caminho não existe
ValueError: invalid modeModo escrito errado
ValueError: I/O operation on closed file.write depois do close() ou fora do bloco with
UnicodeDecodeError: 'utf-8' codec can't decode…Gravado numa codificação, lido em outra: use UTF-8 nos dois
Tudo numa linha só, sem erroFaltou o \n no write/writelines

Exercícios

1. Acrescentar sem apagar

Complete o modo que acrescenta no fim do arquivo:

open("vendas.txt", "", encoding="utf-8")

2. A quebra de linha

Complete para gravar "ovos" numa linha própria:

arquivo.write("ovos")

3. O print no arquivo

Complete o argumento que manda o print para o arquivo:

print("Total: R$ 72.50", =arquivo)

4. O compras.txt tem 10 itens. O que sobra depois de open("compras.txt", "w", encoding="utf-8") com um write("sal\n")?

5. arquivo.writelines(["farinha", "ovos"]) grava o quê?

6. Qual modo protege um arquivo que já existe de ser sobrescrito?

Missão final: não apague o relatório antigo

Missão: o relatorio.txt (veja a aba) já existe, e o programa está apagando o conteúdo dele. Abra com o modo "x" e, num try/except FileExistsError:, mostre O relatório já existe; nada foi apagado. No fim, o arquivo deve continuar com o texto antigo.

with open("relatorio.txt", "w", encoding="utf-8") as arquivo:
    arquivo.write("Relatório novo\n")
print("Relatório gravado.")

with open("relatorio.txt", encoding="utf-8") as arquivo:
    print("Conteúdo:", arquivo.read())
Relatório antigo: vendas de setembro
try:
    with open("relatorio.txt", "x", encoding="utf-8") as arquivo:
        arquivo.write("Relatório novo\n")
    print("Relatório gravado.")
except FileExistsError:
    print("O relatório já existe; nada foi apagado.")

with open("relatorio.txt", encoding="utf-8") as arquivo:
    print("Conteúdo:", arquivo.read())

Resumo

  • Para escrever, o open recebe um modo: "w" apaga e escreve, "a" acrescenta no fim, "x" só cria arquivo novo (senão, FileExistsError).
  • O "w" esvazia o arquivo no momento do open, sem aviso.
  • write e writelines gravam o texto exatamente como está: a quebra de linha \n é por sua conta. E só aceitam texto (str).
  • print(..., file=arquivo) escreve como na tela: separa com espaço, converte números e pula a linha.
  • Sempre encoding="utf-8", e sempre with, que fecha o arquivo sozinho.
  • O "w" cria arquivos, mas não pastas: crie a pasta antes.