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.
- As três formas de ler:
read()(tudo),readline()(uma linha) efor linha in arquivo(todas, uma por vez). - Que cada linha lida termina com
\n, por que aparecem linhas em branco a mais e como ostrip()resolve. - A numerar as linhas com
enumerate()e a ler tudo numa lista comsplitlines(). - 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:
- O primeiro
readline()leu a primeira linha,'arroz\n'. - O segundo continuou de onde o primeiro parou:
'feijão\n'. - O
read()leu o que faltava:'café\nleite\n', e não o arquivo todo. - 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
forchama o equivalente a umreadline()a cada volta e para sozinho quando o arquivo acaba. Sem contador, semwhile. - linha
- Um texto com a linha da vez, com o
\nno fim. O nome é livre (itemserviria), maslinhadeixa claro o que é. - dentro do with
- O
forprecisa estar dentro do bloco dowith: 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.
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ão9.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
| Forma | Devolve | Quando usar |
|---|---|---|
arquivo.read() | Um texto com o arquivo todo | Mostrar o arquivo, procurar uma palavra no texto inteiro; arquivos pequenos |
arquivo.readline() | A próxima linha (com \n); '' no fim | Ler 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 \n | Raramente: quase sempre o splitlines() é melhor |
arquivo.read().splitlines() | Lista de linhas, sem \n | Quando você precisa da lista inteira (contar, ordenar, pegar a última) |
Para tirar o \n | Efeito |
|---|---|
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
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.
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.
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.
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.
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.
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).
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
| Escrita | O 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 |
| Sintoma | Causa provável |
|---|---|
| Linhas em branco entre as linhas na tela | O \n da linha + o do print: use strip() ou end="" |
Uma comparação com a linha nunca dá True | Faltou o strip(): a linha tem \n |
ValueError: not enough values to unpack | Linha vazia ou fora do formato no split |
ValueError: I/O operation on closed file. | Leitura fora do bloco do with |
| A segunda leitura vem vazia | O arquivo já foi lido até o fim |
O for mostra letras soltas | Percorreu o nome do arquivo (faltou o open) |
Exercícios
1. Complete o laço
Complete para mostrar cada linha sem a quebra do fim:
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 noprinte 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 comfloat()/int(). - Linhas vazias quebram o
split(not enough values to unpack): pule-as comif not linha: continue. - Escrever em arquivos é a próxima aula: Escrevendo Arquivos.