Python Lendo Arquivos

Na aula Abrindo Arquivos, você abriu um arquivo com with open(…) e leu tudo de uma vez com read(). Isso basta para mostrar um arquivo na tela. Mas, na maioria das vezes, um arquivo é uma lista de coisas, uma por linha: os itens da compra, as notas da turma, os contatos da agenda. E aí você quer trabalhar linha por linha: contar, somar, procurar. Esta aula mostra as formas de ler um arquivo e o detalhe que pega todo mundo no começo: o \n escondido no fim de cada linha.

Você vai aprender
  • As três formas de ler: read() (tudo), readline() (uma linha) e for linha in arquivo (todas, uma por vez).
  • Que cada linha lida termina com \n, por que aparecem linhas em branco a mais e como o strip() resolve.
  • A numerar as linhas com enumerate() e a ler tudo numa lista com splitlines().
  • A separar os campos de uma linha com split() e calcular a média das notas guardadas num arquivo.
  • A pular as linhas vazias e os erros reais de quem lê arquivos.

Todos os programas desta aula rodaram no Python 3.14 da máquina da Ana (usuária ana, computador notebook), na pasta /home/ana, junto com os arquivos de texto que eles leem. Sempre com encoding="utf-8", como explicado em Abrindo Arquivos.


A ideia: o marcador de página

Pense na lista de compras da Ana, escrita num bloco de papel, um item por linha. Ela pode:

  • olhar a folha inteira de uma vez;
  • ler uma linha, fazer alguma coisa, e depois ler a próxima;
  • passar o dedo pela lista, de linha em linha, até o fim.

O arquivo aberto no Python tem um "dedo", um marcador de posição. Ele começa no início do arquivo e anda conforme você lê. Cada leitura continua de onde a anterior parou; quando o dedo chega ao fim, não há mais nada para ler. Guarde essa imagem, que ela explica quase tudo nesta aula.

O arquivo dos exemplos é a lista de compras, compras.txt:

$ cat compras.txt
arroz
feijão
café
leite

1. read(): tudo de uma vez

with open("compras.txt", encoding="utf-8") as arquivo:
    conteudo = arquivo.read()
print(conteudo)
print(type(conteudo))
print(len(conteudo))
$ python3 ler_tudo.py
arroz
feijão
café
leite

<class 'str'>
24

O read() devolve o arquivo inteiro como um único texto (str), com as quebras de linha dentro dele. São 24 caracteres: as 20 letras dos quatro itens e mais 4 quebras de linha, uma no fim de cada item. A linha em branco antes do <class 'str'> é a última quebra do arquivo somada à do print.

Vendo o \n

No modo interativo (veja O Modo Interativo), quando você escreve só o nome de um valor, o Python mostra o texto com as aspas e os caracteres especiais. É o melhor jeito de ver o que o arquivo tem de verdade:

$ python3 -q
>>> arquivo = open("compras.txt", encoding="utf-8")
>>> arquivo.readline()
'arroz\n'
>>> arquivo.readline()
'feijão\n'
>>> arquivo.read()
'café\nleite\n'
>>> arquivo.read()
''
>>> arquivo.close()
>>> exit()

Agora o \n aparece: é a quebra de linha (veja Caracteres de Escape), um caractere como qualquer outro, que fica no fim de cada linha do arquivo. E repare no dedo andando:

  1. O primeiro readline() leu a primeira linha, 'arroz\n'.
  2. O segundo continuou de onde o primeiro parou: 'feijão\n'.
  3. O read() leu o que faltava: 'café\nleite\n', e não o arquivo todo.
  4. O segundo read() devolveu '', um texto vazio: o dedo já estava no fim.

(No modo interativo, aqui, o arquivo foi aberto sem with, para dar para ver passo a passo; por isso o close() no fim.)


2. readline(): uma linha de cada vez

readline() ("ler linha") devolve a próxima linha do arquivo, com o \n do fim:

with open("compras.txt", encoding="utf-8") as arquivo:
    primeira = arquivo.readline()
    segunda = arquivo.readline()
    print(f"Primeira: {primeira}")
    print(f"Segunda: {segunda}")
$ python3 readline.py
Primeira: arroz

Segunda: feijão

Apareceu uma linha em branco depois de cada item. É o \n que veio do arquivo, dentro de primeira, somado à quebra de linha que o print sempre põe no fim. Duas quebras seguidas = uma linha em branco. A seção 4 resolve isso.

Quando o arquivo acaba, readline() devolve um texto vazio, ''. Como um texto vazio é "falso" num while (veja Booleanos), dá para ler até o fim assim:

with open("compras.txt", encoding="utf-8") as arquivo:
    linha = arquivo.readline()
    while linha:
        print(f"[{linha.strip()}]")
        linha = arquivo.readline()
    print(f"No fim, readline() devolve: [{arquivo.readline()}]")
$ python3 readline_fim.py
[arroz]
[feijão]
[café]
[leite]
No fim, readline() devolve: []

Os colchetes mostram onde cada texto começa e termina: no fim, não havia nada entre eles. Funciona, mas é comprido. Para percorrer todas as linhas, há um jeito bem melhor.


3. for linha in arquivo: todas as linhas

Um arquivo aberto pode ser percorrido com for, como uma lista: a cada volta, a variável recebe a próxima linha.

with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        print(linha)
for linha in arquivo:
"Para cada linha do arquivo." O for chama o equivalente a um readline() a cada volta e para sozinho quando o arquivo acaba. Sem contador, sem while.
linha
Um texto com a linha da vez, com o \n no fim. O nome é livre (item serviria), mas linha deixa claro o que é.
dentro do with
O for precisa estar dentro do bloco do with: fora dele, o arquivo já está fechado.
$ python3 for_linha.py
arroz

feijão

café

leite

Leu tudo, mas com as linhas em branco a mais, pelo mesmo motivo de antes: o \n da linha mais o \n do print.

Por que o for é o preferido

O read() carrega o arquivo inteiro na memória de uma vez. Para uma lista de compras, tanto faz. Mas um arquivo de registros de um sistema pode ter milhões de linhas e vários gigabytes. O for linha in arquivo lê uma linha por vez, então funciona com arquivos de qualquer tamanho.


4. strip(): tirando o \n

O método strip() ("descascar", veja Métodos de Texto) devolve o texto sem os espaços, tabulações e quebras de linha das pontas:

with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        print(linha.strip())
$ python3 for_strip.py
arroz
feijão
café
leite

Agora sim. O strip() tira tudo o que é "branco" das duas pontas, o que costuma ser bom: um espaço esquecido no fim da linha também vai embora. Se você quiser tirar só a quebra de linha e manter os espaços do começo, existe o rstrip("\n") (o r é de right, "direita"):

$ python3 -q
>>> linha = "  café\n"
>>> linha.strip()
'café'
>>> linha.rstrip("\n")
'  café'
>>> exit()

Outra forma: em vez de tirar o \n da linha, pedir ao print para não pôr o dele, com end="" (veja As Opções sep e end):

with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        print(linha, end="")
$ python3 for_end.py
arroz
feijão
café
leite

A tela fica igual. A diferença é que aqui a variável linha continua com o \n; e isso importa quando você vai comparar ou converter a linha, como mostram a seção Quando dá errado e a próxima.

Missão: rode e veja as linhas em branco entre os itens. Conserte o print para a lista sair sem elas (com strip() ou com end="").

with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        print(linha)
arroz
feijão
café
leite
with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        print(linha.strip())

5. Numerando as linhas: enumerate()

Para mostrar o número de cada linha, o enumerate() (veja enumerate() e zip()) entrega, a cada volta, o número e a linha. O start=1 faz a contagem começar do 1, como as pessoas contam:

with open("compras.txt", encoding="utf-8") as arquivo:
    for numero, linha in enumerate(arquivo, start=1):
        print(f"{numero}. {linha.strip()}")
$ python3 numeradas.py
1. arroz
2. feijão
3. café
4. leite

Missão: numere a lista de compras: troque o for por um com enumerate(arquivo, start=1) e mostre 1. arroz, 2. feijão…

with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        print(linha.strip())
arroz
feijão
café
leite
with open("compras.txt", encoding="utf-8") as arquivo:
    for numero, linha in enumerate(arquivo, start=1):
        print(f"{numero}. {linha.strip()}")

6. Todas as linhas numa lista

Às vezes você quer as linhas numa lista, para saber quantas são, pegar a última, ordenar. Há duas formas:

with open("compras.txt", encoding="utf-8") as arquivo:
    linhas = arquivo.readlines()
print(linhas)
with open("compras.txt", encoding="utf-8") as arquivo:
    itens = arquivo.read().splitlines()
print(itens)
print(f"São {len(itens)} itens. O último é {itens[-1]}.")
$ python3 readlines.py
['arroz\n', 'feijão\n', 'café\n', 'leite\n']
['arroz', 'feijão', 'café', 'leite']
São 4 itens. O último é leite.
arquivo.readlines()
"Ler as linhas": uma lista com todas as linhas, cada uma com o seu \n.
arquivo.read().splitlines()
Lê tudo como texto e "parte nas linhas" (splitlines): uma lista sem os \n. Quase sempre é a que você quer.

7. Separando os campos: a média da turma

Agora um arquivo com dados de verdade, as notas da turma, com o nome e a nota em cada linha, separados por ": " (dois-pontos e espaço):

$ cat notas.txt
Ana: 9.0
Bruno: 6.5
Carla: 8.5
Davi: 7.0
soma = 0
quantidade = 0
with open("notas.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        nome, nota = linha.strip().split(": ")
        print(f"{nome} tirou {nota}")
        soma += float(nota)
        quantidade += 1
print(f"Média da turma: {soma / quantidade:.2f}")
linha.strip()
'Ana: 9.0\n' vira 'Ana: 9.0'.
.split(": ")
"Partir" o texto onde houver ": ": 'Ana: 9.0' vira a lista ['Ana', '9.0'].
nome, nota = …
Desempacota a lista de dois itens em duas variáveis (veja Desempacotamento). As duas são textos: nota é '9.0', e não 9.0.
float(nota)
Converte o texto em número para poder somar. Tudo o que vem de um arquivo de texto é texto.
soma / quantidade
A média, depois do laço, com 2 casas (:.2f).
$ python3 media.py
Ana tirou 9.0
Bruno tirou 6.5
Carla tirou 8.5
Davi tirou 7.0
Média da turma: 7.75

Separar campos com split() funciona para arquivos simples como este. Para planilhas com colunas separadas por vírgula, o Python tem um módulo próprio, que entende até vírgulas dentro de aspas: aula Arquivos CSV.


Formas diferentes de fazer a mesma coisa

FormaDevolveQuando usar
arquivo.read()Um texto com o arquivo todoMostrar o arquivo, procurar uma palavra no texto inteiro; arquivos pequenos
arquivo.readline()A próxima linha (com \n); '' no fimLer só a primeira linha (um cabeçalho), ou poucas linhas
for linha in arquivo:Uma linha por volta (com \n)O normal para processar linha por linha; arquivos de qualquer tamanho
arquivo.readlines()Lista de linhas, com \nRaramente: quase sempre o splitlines() é melhor
arquivo.read().splitlines()Lista de linhas, sem \nQuando você precisa da lista inteira (contar, ordenar, pegar a última)
Para tirar o \nEfeito
linha.strip()Tira espaços, tabulações e quebras das duas pontas. O mais usado
linha.rstrip("\n")Tira só a quebra de linha do fim; mantém os espaços
print(linha, end="")Não tira nada: só evita a quebra extra na tela

Quando dá errado

A comparação que nunca dá certo
with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        if linha == "café":
            print("Achei o café!")
print("Fim da busca")
$ python3 sem_strip_cmp.py
Fim da busca

O café está na lista, e o programa não achou. Nenhum erro. A linha lida é 'café\n', e 'café\n' == 'café' é False: o \n conta. Com o strip():

with open("compras.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        if linha.strip() == "café":
            print("Achei o café!")
print("Fim da busca")
$ python3 com_strip_cmp.py
Achei o café!
Fim da busca

Regra prática: quase toda linha lida de um arquivo merece um strip() antes de ser usada.

Uma linha vazia no meio do arquivo

Alguém apertou Enter duas vezes ao digitar as notas:

$ cat notas_buraco.txt
Ana: 9.0
Bruno: 6.5

Carla: 8.5
soma = 0
with open("notas_buraco.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        nome, nota = linha.strip().split(": ")
        soma += float(nota)
print(soma)
$ python3 buraco.py
Traceback (most recent call last):
  File "/home/ana/buraco.py", line 4, in <module>
    nome, nota = linha.strip().split(": ")
    ^^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 1)

"Valores insuficientes para desempacotar (esperava 2, veio 1)." Na linha vazia, ''.split(": ") dá uma lista com um item só (['']), e não dá para repartir um item em duas variáveis. Como consertar: pule as linhas vazias com continue (veja break e continue):

soma = 0
with open("notas_buraco.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        linha = linha.strip()
        if not linha:
            continue
        nome, nota = linha.split(": ")
        soma += float(nota)
print(soma)
$ python3 buraco_ok.py
24.0

Depois do strip(), a linha vazia vira '', que é "falso": o if not linha pula para a próxima volta.

Uma linha fora do formato

No notas_sem2p.txt, a segunda linha ficou Bruno 6.5, sem os dois-pontos:

$ python3 sem2p.py
Ana 9.0
Traceback (most recent call last):
  File "/home/ana/sem2p.py", line 3, in <module>
    nome, nota = linha.strip().split(": ")
    ^^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 1)

A mesma mensagem: sem o ": ", o split não tem onde partir e devolve a linha inteira num item só. O traceback não diz qual linha do arquivo estava ruim (ele fala da linha 3 do programa); a Ana só soube porque o Ana 9.0 apareceu antes. Com enumerate e try/except ValueError (aula try e except), o programa pode avisar "linha 2 do arquivo fora do formato" e seguir para as outras.

Esquecer o open()
for linha in "compras.txt":
    print(linha)
$ python3 nome_nao_arquivo.py
c
o
m
p
r
a
s
.
t
x
t

Sem erro e sem sentido: o programa percorreu as letras do nome do arquivo. "compras.txt" é só um texto; um for num texto passa letra por letra. Como consertar: abrir primeiro, with open("compras.txt", encoding="utf-8") as arquivo:, e percorrer o arquivo.

O for fora do with
with open("compras.txt", encoding="utf-8") as arquivo:
    print("Abri.")
for linha in arquivo:
    print(linha.strip())
$ python3 fechado_for.py
Abri.
Traceback (most recent call last):
  File "/home/ana/fechado_for.py", line 3, in <module>
    for linha in arquivo:
                 ^^^^^^^
ValueError: I/O operation on closed file.

O for está na margem esquerda, fora do bloco: o arquivo já foi fechado. Como consertar: indente o for (e o corpo dele) para dentro do with.

Ler duas vezes
with open("compras.txt", encoding="utf-8") as arquivo:
    primeira_vez = arquivo.read()
    segunda_vez = arquivo.read()
print(f"Primeira leitura: {len(primeira_vez)} caracteres")
print(f"Segunda leitura: {len(segunda_vez)} caracteres")
$ python3 duas_vezes.py
Primeira leitura: 24 caracteres
Segunda leitura: 0 caracteres

A segunda leitura veio vazia: o "dedo" já estava no fim. O mesmo vale para dois for seguidos no mesmo arquivo aberto: o segundo não dá nenhuma volta. Como consertar: guarde o que foi lido numa variável e use a variável quantas vezes quiser (ou abra o arquivo de novo).

E o float() com o \n?

Uma boa notícia: float() e int() ignoram os espaços e a quebra de linha das pontas. float(" 9.0\n") dá 9.0 e int("7\n") dá 7 (conferido no lab). Mesmo assim, use o strip(): ele deixa claro o que você está fazendo e protege as comparações.


Referência rápida

EscritaO que faz
arquivo.read()Lê do ponto atual até o fim, como um texto ('' se já está no fim)
arquivo.readline()Lê a próxima linha, com \n ('' no fim)
for linha in arquivo:Percorre as linhas restantes, uma por volta, com \n
arquivo.readlines()Lista das linhas, com \n
arquivo.read().splitlines()Lista das linhas, sem \n
linha.strip()Tira brancos e quebras das pontas
linha.rstrip("\n")Tira só a quebra do fim
linha.split(": ")Parte a linha nos campos
enumerate(arquivo, start=1)Número e linha, a cada volta
SintomaCausa provável
Linhas em branco entre as linhas na telaO \n da linha + o do print: use strip() ou end=""
Uma comparação com a linha nunca dá TrueFaltou o strip(): a linha tem \n
ValueError: not enough values to unpackLinha vazia ou fora do formato no split
ValueError: I/O operation on closed file.Leitura fora do bloco do with
A segunda leitura vem vaziaO arquivo já foi lido até o fim
O for mostra letras soltasPercorreu o nome do arquivo (faltou o open)

Exercícios

1. Complete o laço

Complete para mostrar cada linha sem a quebra do fim:

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

2. O que tem na linha?

O arquivo tem a linha pão (seguida de outras). O que arquivo.readline() devolve na primeira chamada, como o modo interativo mostraria (com as aspas)?

3. Por que for linha in arquivo: print(linha) mostra uma linha em branco entre os itens?

4. Qual forma lê um arquivo de 5 GB sem precisar de 5 GB de memória?

5. Depois de um arquivo.read(), o que devolve um segundo arquivo.read() no mesmo arquivo aberto?

Missão final: a média com uma linha vazia

Missão: o arquivo notas.txt (na outra aba) tem uma linha vazia no meio, e o programa quebra nela. Faça o laço tirar o \n com strip() e pular as linhas vazias (if not linha: continue), para chegar à Média da turma: 7.75.

soma = 0
quantidade = 0
with open("notas.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        nome, nota = linha.split(": ")
        soma += float(nota)
        quantidade += 1
print(f"Média da turma: {soma / quantidade:.2f}")
Ana: 9.0
Bruno: 6.5

Carla: 8.5
Davi: 7.0
soma = 0
quantidade = 0
with open("notas.txt", encoding="utf-8") as arquivo:
    for linha in arquivo:
        linha = linha.strip()
        if not linha:
            continue
        nome, nota = linha.split(": ")
        soma += float(nota)
        quantidade += 1
print(f"Média da turma: {soma / quantidade:.2f}")

Resumo

  • O arquivo aberto tem um marcador de posição: cada leitura continua de onde a anterior parou; no fim, as leituras devolvem ''.
  • read() lê tudo num texto; readline() lê uma linha; for linha in arquivo: percorre as linhas, uma por vez, e é a forma preferida.
  • Toda linha lida termina com \n: por isso as linhas em branco a mais no print e as comparações que falham. linha.strip() resolve.
  • enumerate(arquivo, start=1) numera as linhas; read().splitlines() dá uma lista sem os \n.
  • Tudo o que vem de um arquivo de texto é texto: separe os campos com split() e converta com float()/int().
  • Linhas vazias quebram o split (not enough values to unpack): pule-as com if not linha: continue.
  • Escrever em arquivos é a próxima aula: Escrevendo Arquivos.