MAC2166 - Introdução à Computação

06/06/2017 - Aula 19

Leitura de Arquivos

Nos exercícios que fizemos no curso até a aula de hoje, sempre criamos programas que manipulavam dados digitados pelos usuários. Entretanto, essa não é a unica forma de entrada de dados que podemos usar em um programa. Uma outra forma de entrada de dados usada com frequência em programas é a leitura de dados gravados em um arquivo.

Para abrir um arquivo para leitura, podemos usar a função open(), passando como parâmetros para ela o nome (com caminho, se necessário) do arquivo a ser aberto e a string 'r' (de read, para indicar que o arquivo será aberto para leitura). A função devolve um objeto do tipo file.

Para ler o conteúdo inteiro de um arquivo var_arquivo já aberto para leitura, podemos usar a função var_arquivo.read(), que devolve uma string (str).

Depois que o uso de um arquivo já tiver sido encerrado, é preciso fechar o arquivo por meio da função close().

Veja o exemplo a seguir, que abre um arquivo chamado 'meu_arquivo.txt', lê cada linha do arquivo e a imprime.

In [38]:
nome_do_arquivo = "meu_arquivo.txt"
arquivo = open(nome_do_arquivo, 'r')  # abre o arquivo para leitura
print("Conteúdo do arquivo '%s'\n" %nome_do_arquivo)

conteudo = arquivo.read()       # lê o conteúdo do arquivo
print(conteudo)
    
arquivo.close()  # depois do uso, fecha o arquivo
Conteúdo do arquivo 'meu_arquivo.txt'

    Para ser grande, sê inteiro: nada
    Teu exagera ou exclui.
    Sê todo em cada coisa. Põe quanto és
    No mínimo que fazes.
    Assim em cada lago a lua toda
    Brilha, porque alta vive.

        Ricardo Reis, 14-2-1933 
        (heterônimo de Fernando Pessoa)	



Quando o arquivo é muito grande, ler o seu conteúdo inteiro de uma só vez não é uma boa ideia, pois ele pode ocupar toda a memória do computador. Nesse caso, o melhor é ler uma linha do arquivo por vez.

Para ler uma linha de um arquivo var_arquivo já aberto para leitura, podemos usar a função var_arquivo.readline(), que também devolve uma string (str). Quando se está no final do arquivo, a função readline() devolve uma string vazia ('').

In [33]:
nome_do_arquivo = "meu_arquivo.txt"
arquivo = open(nome_do_arquivo, 'r')  # abre o arquivo para leitura
print("Conteúdo do arquivo '%s'\n" %nome_do_arquivo)

linha = arquivo.readline()      # lê uma linha do arquivo
while linha != "":              # enquanto não chegou ao final do arquivo
    print(linha,end="")             # imprime a linha
    linha = arquivo.readline()      # lê uma nova linha do arquivo
    
arquivo.close()  # depois do uso, fecha o arquivo
Conteúdo do arquivo 'meu_arquivo.txt'

    Para ser grande, sê inteiro: nada
    Teu exagera ou exclui.
    Sê todo em cada coisa. Põe quanto és
    No mínimo que fazes.
    Assim em cada lago a lua toda
    Brilha, porque alta vive.

        Ricardo Reis, 14-2-1933 
        (heterônimo de Fernando Pessoa)	


Para fazer um laço que lê todas as linhas de um arquivo (uma por vez), também podemos usar o comando for var_linha in arquivo:, onde var_linha é o nome da variável que armazenará o conteúdo de uma linha e var_arquivo é o arquivo já aberto para leitura, como ilustrado abaixo:

In [34]:
nome_do_arquivo = "meu_arquivo.txt"
arquivo = open(nome_do_arquivo, 'r', encoding="utf8")  # abre o arquivo para leitura
print("Conteúdo do arquivo '%s'\n" %nome_do_arquivo)

for linha in arquivo:      # percorre cada linha do arquivo
    print(linha,end="")    # imprime a linha
    
arquivo.close()  # depois do uso, fecha o arquivo
Conteúdo do arquivo 'meu_arquivo.txt'

    Para ser grande, sê inteiro: nada
    Teu exagera ou exclui.
    Sê todo em cada coisa. Põe quanto és
    No mínimo que fazes.
    Assim em cada lago a lua toda
    Brilha, porque alta vive.

        Ricardo Reis, 14-2-1933 
        (heterônimo de Fernando Pessoa)	


No exemplo acima, passamos um terceiro parâmetro para a função open(), o parâmetro chamado encoding. Esse parâmetro é opcional; quando não passamos nenhum valor para ele, o valor usado é a codificação padrão do Python 3, que é 'utf8'. Um outro valor possível para o parâmetro encoding é o 'latin1'. O exemplo abaixo lê o arquivo 'meu_arquivo_latin1.txt', que tem codificação latin1.

In [37]:
nome_do_arquivo = "meu_arquivo_latin1.txt"
arquivo = open(nome_do_arquivo, 'r', encoding="latin1")  # abre o arquivo para leitura
print("Conteúdo do arquivo '%s'\n" %nome_do_arquivo)

for linha in arquivo:      # percorre cada linha do arquivo
    print(linha,end="")    # imprime a linha
    
arquivo.close()  # depois do uso, fecha o arquivo
Conteúdo do arquivo 'meu_arquivo_latin1.txt'

    Para ser grande, sê inteiro: nada
    Teu exagera ou exclui.
    Sê todo em cada coisa. Põe quanto és
    No mínimo que fazes.
    Assim em cada lago a lua toda
    Brilha, porque alta vive.

        Ricardo Reis, 14-2-1933 
        (heterônimo de Fernando Pessoa)	


É possível evitar de ter que fechar explicitamente um arquivo (com close()) usando o comando with (...) as <nome_var> : na sua abertura, como no exemplo abaixo. Ao final do corpo do with, o comando with fecha automaticamente o arquivo.

In [39]:
nome_do_arquivo = "meu_arquivo.txt"

with open(nome_do_arquivo, 'r') as arquivo:     # abre o arquivo para leitura
    # Corpo do WITH
    print("Conteúdo do arquivo '%s'\n" %nome_do_arquivo)

    for linha in arquivo:      # percorre cada linha do arquivo
        print(linha,end="")    # imprime a linha

print("Fim!")
Conteúdo do arquivo 'meu_arquivo.txt'

    Para ser grande, sê inteiro: nada
    Teu exagera ou exclui.
    Sê todo em cada coisa. Põe quanto és
    No mínimo que fazes.
    Assim em cada lago a lua toda
    Brilha, porque alta vive.

        Ricardo Reis, 14-2-1933 
        (heterônimo de Fernando Pessoa)	

Fim!

Importante: Para poder reler a partir da primeira linha um arquivo que está aberto e já foi lido, é preciso reabri-lo com a função open(). Todo arquivo aberto tem um marcador que indica a posição do primeiro caracter que ainda não foi lido do arquivo (ou seja, a posição de início da próxima leitura a ser realizada). A função open() põe o marcador no início do arquivo. E a cada vez que chamamos a função read() ou a função readline(), o marcador se move para o caractere que vem em seguida do último caractere retornado. No caso de readline, o marcador se move para o primeiro caractere da próxima linha. No caso de read, o marcador se move para o final do arquivo.

Gravação de Arquivos

Também podemos usar a função open() para criar um arquivo para a gravação de dados, passando como parâmetros para ela o nome (com caminho, se necessário) do arquivo a ser criado e a string 'w' (de write, para indicar que o arquivo será aberto para escrita).

Para gravar uma string em um arquivo var_arquivo já aberto para escrita, podemos usar a função var_arquivo.write().

Assim como na leitura de arquivos, depois que o uso de um arquivo aberto para escrita já tiver sido encerrado, é preciso fechar o arquivo por meio da função close().

Veja o exemplo a seguir, que cria um arquivo chamado 'meu_poema.txt' e grava um poema nele.

In [20]:
nome_do_arquivo = "meu_poema.txt"
arquivo = open(nome_do_arquivo, 'w')     # Cria ou abre o arquivo para escrita

poeminha = ["Batatinha quando nasce","Espalha a rama pelo chão","Menininha quando dorme"]

for linha in poeminha:
    arquivo.write(linha)   # grava uma linha do poema no arquivo
    arquivo.write('\n')    # coloca uma quebra de linha no final da linha do poema
    
arquivo.close()  # depois do uso, fecha o arquivo

Quando abrimos um arquivo com a opção de escrita 'w', se o arquivo não existe previamente, ele será criado pela função open(). Entretanto, caso o arquivo já exista, o seu conteúdo atual será sobreescrito (ou seja, perdido).

Para acrescentarmos conteúdo no final de um arquivo que já existe, podemos abrir o arquivo com a função open() passando como parâmetros para ela o nome (com caminho, se necessário) do arquivo a ser aberto e a string 'a' (de append, para indicar que o arquivo será aberto para escrita preservando o seu conteúdo atual). Caso o arquivo que se tenta abrir com opção 'a' não existe previamente, ele será criado pela função open().

Para gravar uma string no final de um arquivo aberto com a opção 'a', também usamos a função write().

Veja o exemplo a seguir, que adiciona novas frases ao final do arquivo chamado 'meu_poema.txt':

In [24]:
nome_do_arquivo = "meu_poema.txt"
arquivo = open(nome_do_arquivo, 'a')   # Abre o arquivo para append (acréscimo)

fim_poeminha = "Põe a mão no coração"
arquivo.write(fim_poeminha)  
    
arquivo.close()  # depois do uso, fecha o arquivo

"Captura" e "Tratamento" de Exceções

Quando tentamos usar a função open() para abrir para leitura um arquivo que não existe ou passamos um caminho inválido para um arquivo a ser aberto, a função gera o erro FileNotFoundError, como mostrado abaixo:

In [2]:
nome_do_arquivo = "bla_bla.txt"  # nome que não corresponde a um arquivo existente
arquivo = open(nome_do_arquivo, 'r')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-2-abf2101a88d2> in <module>()
      1 nome_do_arquivo = "bla_bla.txt"  # nome que não corresponde a um arquivo existente
----> 2 arquivo = open(nome_do_arquivo, 'r')

FileNotFoundError: [Errno 2] No such file or directory: 'bla_bla.txt'

O programa abaixo pede que o usuário digite o nome de um arquivo a ser aberto, mas caso o usuário digite um nome de arquivo que não existe, em vez da execução da função ser interrompida pelo erro FileNotFound (gerado pela tentativa de abertura do arquivo), o programa pede para que o usuário digite novamente um nome de arquivo e tenta abri-lo novamente. O programa repete essas operações até que o usuário digite o nome de um arquivo que exista.

In [40]:
# Lê o nome do arquivo de dados e o abre para leitura
abriu = False
while not abriu:
    nome_do_arquivo = input("Digite o nome do arquivo de dados: ")
    
    try: 
        arquivo = open(nome_do_arquivo, 'r') 
    except FileNotFoundError:
        print(nome_do_arquivo, " não encontrado. Você digitou direito?")
    else:
        abriu = True
        
print("Conteúdo do arquivo '%s'\n" %nome_do_arquivo)

for linha in arquivo:
    print(linha,end="")
    
arquivo.close()  # depois do uso, fecha o arquivo
Digite o nome do arquivo de dados: arquivo.txt
arquivo.txt  não encontrado. Você digitou direito?
Digite o nome do arquivo de dados: meu_arq.txt
meu_arq.txt  não encontrado. Você digitou direito?
Digite o nome do arquivo de dados: meu_arquivo.txt
Conteúdo do arquivo 'meu_arquivo.txt'

    Para ser grande, sê inteiro: nada
    Teu exagera ou exclui.
    Sê todo em cada coisa. Põe quanto és
    No mínimo que fazes.
    Assim em cada lago a lua toda
    Brilha, porque alta vive.

        Ricardo Reis, 14-2-1933 
        (heterônimo de Fernando Pessoa)	


Para evitar que a execução do programa seja interrompida quando o erro FileNotFoundError acontece, o programa usa o comando para tratamento de exceções try.

O Python começa tentando executar o bloco de comandos associado à cláusula try e caso tudo corra bem (no exemplo acima, se o arquivo existe e foi aberto para leitura com sucesso), então pula o bloco de comandos associado à cláusula except e executa o bloco de comandos associado à cláusula else. Mas se o erro FileNotFoundError ocorrer durante a execução dos comandos no try, então o bloco de comandos associado à cláusula except é executado e depois o bloco de comandos do else é pulado.

A cláusula else é opcional. Portanto, podemos ter um comando try só com except. Além disso, no except podemos não especificar o tipo de erro que se deseja capturar. Nesse caso, qualquer erro que for gerado durante a execução dos comandos no bloco do try causará a execução dos comandos no bloco do except.

Veja outro exemplo a seguir:

In [4]:
divisor = 0
try:
    print("Agora eu vou dividir...")
    divisao = 100 / divisor
    print("Resultado da divisão:", divisao )
except:
    print("Não consegui calcular a divisão!")
    
Agora eu vou dividir...
Não consegui calcular a divisão!

Para saber mais sobre arquivos ...

... consulte a seguinte referência:

E para saber mais sobre tratamento de exceções ...

... consulte a seguinte referência:

Problema 19.1

Escreva um programa que lê um arquivo e o imprime com as linhas numeradas.

Solução 1

In [3]:
def main():
    nome_do_arquivo = input("Nome do arquivo: ")
    arquivo = open(nome_do_arquivo, 'r', encoding='utf8')
    nlin = 1
    for linha in arquivo:
        print(nlin, ':', linha)
        nlin += 1

    arquivo.close()
    
    
main()
Nome do arquivo: meu_arquivo_utf8.txt
1 :     Para ser grande, sê inteiro: nada

2 :     Teu exagera ou exclui.

3 :     Sê todo em cada coisa. Põe quanto és

4 :     No mínimo que fazes.

5 :     Assim em cada lago a lua toda

6 :     Brilha, porque alta vive.

7 : 

8 :         Ricardo Reis, 14-2-1933 

9 :         (heterônimo de Fernando Pessoa)	

10 : 


A impressão gerada pela solução acima não ficou muito boa. As linhas estão separadas por uma linha em branco e os números não ficaram alinhados.

O problema das linhas em branco acontece porque quando lemos cada linha do arquivo, ela já vem com uma quebra de linha no final. E quando usamos o comando print exibir a linha, ele inclui uma outra quebra de linha. Então, para resolver o problema, podemos usar o print passando para ele o parâmetro end='', como mostrado na Solução 2.

Solução 2

In [41]:
def main():
    nome_do_arquivo = input("Nome do arquivo: ")
    arquivo = open(nome_do_arquivo, 'r', encoding='utf8')
    nlin = 1
    for linha in arquivo:
        print('%4d: %s' %(nlin,linha), end="")
        nlin += 1

    arquivo.close()
    
    
main()
Nome do arquivo: meu_arquivo.txt
   1:     Para ser grande, sê inteiro: nada
   2:     Teu exagera ou exclui.
   3:     Sê todo em cada coisa. Põe quanto és
   4:     No mínimo que fazes.
   5:     Assim em cada lago a lua toda
   6:     Brilha, porque alta vive.
   7: 
   8:         Ricardo Reis, 14-2-1933 
   9:         (heterônimo de Fernando Pessoa)	
  10: 

Agora a impressão ficou correta. :)

Mas temos ainda uma terceira alternativa de solução, que elimina a quebra de linha do final de uma linha lida do arquivo. Essa solução envolve o uso da função rstrip().

Solução 3

In [42]:
def main():
    nome_do_arquivo = input("Nome do arquivo: ")
    arquivo = open(nome_do_arquivo, 'r', encoding='utf8')
    nlin = 1
    for linha in arquivo:
        linha = linha.rstrip()
        print('%4d: %s' %(nlin,linha))
        nlin += 1

    arquivo.close()
    
    
main()
Nome do arquivo: meu_arquivo.txt
   1:     Para ser grande, sê inteiro: nada
   2:     Teu exagera ou exclui.
   3:     Sê todo em cada coisa. Põe quanto és
   4:     No mínimo que fazes.
   5:     Assim em cada lago a lua toda
   6:     Brilha, porque alta vive.
   7: 
   8:         Ricardo Reis, 14-2-1933
   9:         (heterônimo de Fernando Pessoa)
  10: 

A chamada S.rstrip() que devolve uma cópia da string S sem os "caracteres brancos" que ela tiver em seu final. São considerados caracteres brancos os caracteres espaço (' '), tabulação (\t) e quebra de linha (\n).

Temos também as funções lstrip() (que devolve uma cópia da string eliminando todos os caracteres brancos do início da string) e lstrip() (que devolve uma cópia da string eliminando todos os caracteres brancos do início e do fim da string). Veja os exemplos abaixo:

In [10]:
texto = "\n\n  \t  Pequeno texto de teste! \t\t \n\n"
print(texto)
print("--------------")
print(texto.rstrip())
print("--------------")
print(texto.lstrip())
print("--------------")
print(texto.strip())
print("--------------")


  	  Pequeno texto de teste! 		 


--------------


  	  Pequeno texto de teste!
--------------
Pequeno texto de teste! 		 


--------------
Pequeno texto de teste!
--------------

Mas agora, voltando ao problema 19.1: e se o nome de arquivo digitado pelo usuário não corresponde a um arquivo que existe? Seria interessante avisar o usuário sobre o problema e permitir que ela escolha um outro arquivo. A Solução 4 faz isso.

Solução 4

In [43]:
def main():
    abriu = False
    while not abriu:
        nome_do_arquivo = input("Digite o nome do arquivo de dados: ")

        try: 
            arquivo = open(nome_do_arquivo, 'r', encoding = 'utf8') 
        except FileNotFoundError:
            print(nome_do_arquivo, " não encontrado. Você digitou direito?")
        else:
            abriu = True

    nlin = 1
    for linha in arquivo:
        linha = linha.rstrip()
        print('%4d: %s' %(nlin,linha))
        nlin += 1

    arquivo.close()
    
    
main()
Digite o nome do arquivo de dados: meu_arquivo.txt
   1:     Para ser grande, sê inteiro: nada
   2:     Teu exagera ou exclui.
   3:     Sê todo em cada coisa. Põe quanto és
   4:     No mínimo que fazes.
   5:     Assim em cada lago a lua toda
   6:     Brilha, porque alta vive.
   7: 
   8:         Ricardo Reis, 14-2-1933
   9:         (heterônimo de Fernando Pessoa)
  10: 

Problema 19.2

Escreva um programa que leia um arquivo onde cada linha contém o nome e as notas de um estudante e, para cada estudante, calcule e imprima a média das notas. Ao final, o programa deve imprimir também a média da turma.

Exemplo: para o arquivo notas.txt com:

jose 10 15 20 30 40 pedro 23 16 19 22 suzana 8 22 17 14 32 17 24 21 2 9 11 17 gisela 12 28 21 45 26 10 joao 14 32 25 16 89

a saída deve ser:

Digite o nome do arquivo: notas.txt Aluno: jose Média = 23.000000 Aluno: pedro Média = 20.000000 Aluno: suzana Média = 16.166667 Aluno: gisela Média = 23.666667 Aluno: joao Média = 35.200000 Média da turma = 23.606667

In [32]:
def main():
    nome = input("Digite o nome do arquivo: ")

    try:
        arquivo = open(nome, 'r')   # abre o arquivo para leitura
    except:
        print("Não consegui abrir o arquivo %s" %(nome))
    else:
        total = 0
        cont_alunos = 0
        for linha in arquivo:
            dados = linha.split()
            notas = dados[1:]
            soma = 0
            for n in notas:
                soma += float(n)
                
            media = soma/len(notas)
            print("Aluno: %s \t Média = %f"%(dados[0],media))
            
            total += media
            cont_alunos += 1

        print("Média da turma = %f"%(total/cont_alunos))

        arquivo.close()  # fecha o arquivo 

main()
Digite o nome do arquivo: notas.txt
Aluno: jose 	 Média = 23.000000
Aluno: pedro 	 Média = 20.000000
Aluno: suzana 	 Média = 16.166667
Aluno: gisela 	 Média = 23.666667
Aluno: joao 	 Média = 35.200000
Média da turma = 23.606667

Obs.: Quando fazemos uma chamada do tipo S.split(), ou seja, chamamos a split a partir de uma string S sem passar para a função nenhum separador, a função separará S nos caracteres brancos (espaços, tabulações e quebras de linhas); além disso, as strings vazias são removidas da lista de resultado. Veja os exemplos abaixo:

In [13]:
texto = "Para ser  grande,\t sê inteiro:\tnada\nTeu exagera    ou exclui."
print(texto)
print("----------")
print("Separando pelo caracter de espaço: ", texto.split(" "))
print("Separando pelo caracter de tabulação: ", texto.split("\t"))
print("Separando pelos caracteres brancos: ", texto.split())
Para ser  grande,	 sê inteiro:	nada
Teu exagera    ou exclui.
----------
Separando pelo caracter de espaço:  ['Para', 'ser', '', 'grande,\t', 'sê', 'inteiro:\tnada\nTeu', 'exagera', '', '', '', 'ou', 'exclui.']
Separando pelo caracter de tabulação:  ['Para ser  grande,', ' sê inteiro:', 'nada\nTeu exagera    ou exclui.']
Separando pelos caracteres brancos:  ['Para', 'ser', 'grande,', 'sê', 'inteiro:', 'nada', 'Teu', 'exagera', 'ou', 'exclui.']

Tópicos vistos na aula 19

  • Leitura e escrita de arquivos
  • Brevíssima introdução à captura e ao tratamento de exceções com o comando try