Pular para o conteúdo principal

Exercícios da aula 06

Exercícios da aula 06: Arquivos

Na aula 06, terminamos as funções e introduzimos a escrita e leitura de arquivos. Os exercícios referentes a funções já foram resolvidos junto com os exercícios da aula 05.

Exercício 1

Escreva um programa que receba o nome de um arquivo pela linha de comando e imprimia todas as linhas desse aqruivo

O programa pode ser visto no arquivo exercicio01.py:

#! /usr/bin/env python3

import sys


def imprimir_linhas(arquivo):
    with open(arquivo, "r") as arq:
        for linha in arq.readlines():
            print(linha.strip())


if __name__ == '__main__':
    arquivo = sys.argv[1]
    imprimir_linhas(arquivo)

Para se executar o script acima, pode-se fazer o seguinte:

pjabardo@makhno> python3 exercicio01.py arquivo-ex01.txt 
Primeira linha
Segunda
Terceira
...
Última linha
pjabardo@makhno:~/Documents/assipt/pythonassipt/06-aula$

Essa solução vai funcionar tanto no Windows quanto no Linux ou MacOS ou qualquer Unix.

Script como executável no Linux (e MacOS?)

Muitas vezes é conveniente usar o script python como se fosse um programa executável executável (.exe no windows). A linha #! /usr/bin/env python3 é para especificar, no unix (linux, macos) qual o interpretador que deve ser usado. Para que seja usado como executável, ainda é preciso avisar o sistema operacional para tratar o arquivo como executável:

pjabardo@makhno> chmod +x exercicio01.py
Script como executável no Windows

É bem parecido com o Linux mas na primeira linha deve-se substituir #! /usr/bin/env python3 por #!"C:\Python39\Python.exe". Ajuste para o caminho do executável do Python que tem no teu sistema.

Executando o script "executável":
pjabardo@makhno> ./exercicio01.py arquivo-ex01.txt 
Primeira linha
Segunda
Terceira
...
Última linha
In [5]:
imprimir_linhas("arquivo-ex01.txt")
Primeira linha
Segunda
Terceira
...
Última linha

Observe que nessa solução, a funcionalidade está implementada dentro da função imprimir_linhas. Com isso, podemos reutilizar o código. É por isso que existe o trecho

if __name__ == '__main__':
    arquivo = sys.argv[1]
    imprimir_linhas(arquivo)

Caso o if seja verdadeiro, vamos executar essa trecho. Esse if é verdadeiro se estamos usando o arquivo como um script.

O arquivo também pode ser usado como um módulo. E aí que vem a vantagem de escrever este programa desse jeito:

In [7]:
# Usando o programa como um módulo:
from exercicio02 import imprimir_linhas
In [8]:
imprimir_linhas("arquivo-ex01.txt")
Primeira linha
Segunda
Terceira
...
Última linha
In [ ]:
 

A idéia é sempre tentar reaproveitar código o máximo possível!

In [ ]:
 

Exercício 2

Modifique o programa do exercício de tal modo que receba mais dois parâmetros: a linha de incício e a de fim de impressão. O programa deve imprimir apenas as linhas entre esses dois valores (inlcuindo as linhas de incício e fim)

A minha solução pode ser vista a seguir. Ela se encontra no arquivo exercicio02.py e tem algumas novidades.

import argparse


def imprimir_linhas(arquivo, linha_inicio=0, linha_fim=-1):
    with open(arquivo, "r") as arq:
        i = 0
        for linha in arq:
            if i >= linha_inicio and (linha_fim < 0 or i < linha_fim):
                print(linha.strip())
            i = i + 1
            if i == linha_fim:
                break


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Imprimir linhas")
    parser.add_argument('arquivo', help="Nome do arquivo", type=str, nargs=1)
    parser.add_argument("-i", "--inicio", help="Número da primeira linha para imprimir 0 caso não seja fornecido.", default=0, type=int)
    parser.add_argument("-f", "--fim", help="Número da última que deve ser impressa. -1 para imprimir até o final", default=-1, type=int)

    args = parser.parse_args()

    imprimir_linhas(args.arquivo[0], args.inicio, args.fim)

A estrutura do código é a mesma que o programa do programa anterior. Temos uma função que implementa a funcionalidade que desejamos (função imprimir_linhas) e temos o trecho

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Imprimir linhas")
    # ...

Assim o código pode ser usado como módulo e como script (programa).

A primeira novidade é que o programa usa o módulo argparse da biblioteca padrão do Python. Este módulo ajuda a interpretar os argumentos de linha de comando. É muito melhor do que usar sys.argv diretamente como fizemos no exercício 01.

Vejamos a funcionalidade do código:

pjabardo@makhno> ./exercicio02.py --help
usage: exercicio02.py [-h] [-i INICIO] [-f FIM] arquivo

Imprimir linhas

positional arguments:
  arquivo               Nome do arquivo

optional arguments:
  -h, --help            show this help message and exit
  -i INICIO, --inicio INICIO
                        Número da primeira linha para imprimir 0 caso não seja fornecido.
  -f FIM, --fim FIM     Número da última que deve ser impressa. -1 para imprimir até o final

Ao usar o módulo argparse já temos um help! Como funciona? Primeiro é necessário criar o parser que vai interpretar as opções. O parser é inicializado na linha

parser = argparse.ArgumentParser(description="Imprimir linhas")

A descrição (description) é o que aparece no help. Aí temos que definir os argumentos de linha de comando. O jeito que eu fiz o programa, temos que fornecer o nome de um arquivo e isso é implementado na linha

parser.add_argument('arquivo', help="Nome do arquivo", type=str, nargs=1)

Teremos um argumento chamado arquivo que é do tipo str e é composto por apenas um argumento (nargs=1).

Para dizer em que linha se deve começar a imprimir, usa-se a opção --inicio ou -i definido na linha

parser.add_argument("-i", "--inicio", help="Número da primeira linha para imprimir 0 caso não seja fornecido.", default=0, type=int)

O argumento especificando a linha inicial que deve ser impressa é opcional tem duas formas: a curta -i ou a extensa --inicio. O tipo de dado vai ser um int e caso este argumento não seja fornecido, o valor usado é 0 (começamos da primeir linha).

O argumento especificando a linha final que deve ser impressa é também opcional. Neste caso, se nada for escrito, o valor -1 -e adotado. Este valor sinaliza que queremos imprimir até o final.

parser.add_argument("-f", "--fim", help="Número da última que deve ser impressa. -1 para imprimir até o final", default=-1, type=int)

A linha seguinte:

args = parser.parse_args()

aplica o parser que definimos aos argumentos de linha de comando (sys.argv).

Para acessar os argumentos, basta usar os campos de args:

  • args.arquivo para o nome do arquivo
  • args.inicio para o número de linha inicial
  • args.fim para o número de linha final.

Esses argumentos são então passados para a função imprimir_linhas (vamos destrinchar mais adiante).

Vamos ver como funciona:

Se não fornecermos nenhuma opção, ocorre um erro:

pjabardo@makhno> ./exercicio02.py 
usage: exercicio02.py [-h] [-i INICIO] [-f FIM] arquivo
exercicio02.py: error: the following arguments are required: arquivo
pjabardo@makhno:~/Documents/assipt/pythonassipt/06-aula$

Fornecendo apenas o nome do arquivo, imprimimos todas as linhas.

pjabardo@makhno> ./exercicio02.py arquivo-ex01.txt 
Primeira linha
Segunda
Terceira
...
Última linha

Só a opção -i:

pjabardo@makhno> ./exercicio02.py -i1 arquivo-ex01.txt 
Segunda
Terceira
...
Última linha

pode ser também

pjabardo@makhno> ./exercicio02.py --inicio 2 arquivo-ex01.txt 
Terceira
...
Última linha

Nos exemplos acima, -f ou --fim fornecem o valor padrão -1 que representa imprimir até o final do arquivo

pjabardo@makhno> ./exercicio02.py --inicio 2 --fim 4 arquivo-ex01.txt 
Terceira
...

ou

pjabardo@makhno> ./exercicio02.py -i 1 -f 3 arquivo-ex01.txt 
Segunda
Terceira

Observe que o parser vai ver se o tipo é compatível:

pjabardo@makhno> ./exercicio02.py -i um
usage: exercicio02.py [-h] [-i INICIO] [-f FIM] arquivo
exercicio02.py: error: argument -i/--inicio: invalid int value: 'um'

Olha que legal: reconheceu que um não é um número e deu erro!

Vamos analisar agora o código em si que tem algumas coisas interessantes.

def imprimir_linhas(arquivo, linha_inicio=0, linha_fim=-1):
    with open(arquivo, "r") as arq:
        i = 0
        for linha in arq:
            if i >= linha_inicio and (linha_fim < 0 or i < linha_fim):
                print(linha.strip())
            i = i + 1
            if i == linha_fim:
                break

A função tem três argumentos:

  • arquivo uma string com o nome dos arquivos
  • linha_inicio um argumento opcional com valor padrão 0 que indica a primeira linha a ser impressa
  • linha_fim um argumento opcional que indica a última linha a ser impressa. Se for -1, imprime até o final

O código usa with open(arquivo, "r") as arq para abrir o arquivo para leitura, como a maioria dos programas até agora.

Agora que as coisas são um pouco diferentes. Até agora usamos o método arq.readlines() que lê todas as linhas de um arquivo. Prático e útil mas e se o arquivo for gigantesco e precisamos só de algumas linhas no começo? Vamos gastar um monte de memória para nada.

Uma opção é usar o método arq.readline() que lê uma linha de cada vez. Mas existe uma possibilidade mais interessante: usar o o tipo arquivo como um iterador. Aí acessamos diretamente no for linha a linha. Temos um contador i para contar as linhas já lidas e só imprimimos quando i >= linha_inicio.

Agora temos que ter cuidado para acabar a impressão. Temos dois casos:

  • linha_fim < 0 Vamos até o final do arquivo
  • linha_fim >= 0 vamos contar até chegar nesse limite.

Exercício 3

Crie um programa que receba o nome de dois arquivos como parâmetros da linha de comando e que gere um arquivo de saída com as linhas do primeiro e do segundo arquivo

Não vou fazer um programa, apenas uma função:

In [10]:
def imprime_arquivos(arquivos_entrada, arquivo_saída):
    
    with open(arquivo_saída, "w") as fout:
        
        for arq in arquivos_entrada:
            with open(arq, "r") as fin:
                for linha in fin:
                    fout.write(linha)
In [11]:
imprime_arquivos(["arquivo-ex01.txt", "outro-arquivo.txt"], "saida-exercicio-03.txt")

O resultado pode ser visto a seguir:

Primeira linha
Segunda
Terceira
...
Última linha
Outra linha
Mais outra
Mais um montão de linhas
A última linha

Exercício 4

Crie um programa que recebe o nome de um arquivo e o nome do arquivo de saída e que inverta as linhas do primeiro arquivo e armazene no segundo.

Novamente não vou fazer o programa, apenas uma função.

In [12]:
def reverter_arquivo(arquivo_entrada, arquivo_saída):
    with open(arquivo_entrada, "r") as fin:
        linhas = fin.readlines()
    linhas.reverse()
    with open(arquivo_saída, "w") as fout:
        for linha in linhas:
            fout.write(linha)
In [13]:
reverter_arquivo("arquivo-ex01.txt", "arquivo-reverso.txt")

A solução acima é simples mas imagina se você tiver arquivos enormes que não cabem na memória. É possível arranjar soluções optimizadas para estes casos mas não é tão simples assim. Você consegue imaginar por quê?

Última linha
...
Terceira
Segunda
Primeira linha

Exercício 5

Modofique o programa anterior para utilizar o elemento p em vez de h2

Vou criar uma função que encapsula tudo isso. O tipo de elemento pode ser fornecido pelo usuário.

In [ ]:
 
In [15]:
filmes = {
    "drama" : ["O Homem Elefante", "O Poderoso Chefão"],
    "comédia" : ["Se beber não case", "O Auto da Compadecida", "American Pie"],
    "policial" : ["Seven", "O silêncio dos inocentes", "Os Homens que não Amavam as Mulheres"],
    "guerra" : ["Stalingrado", "O Resgate do Soldado Ryan", "Dunquerque"],
    "ficção científica" : ["O Expresso do Amanhã", "Duna", "Guerra nas Estrelas", "A Ira de Kahn"]
}
In [19]:
def filmes_html(filmes, arquivo="filmes.html", elemento="p"):
    with open(arquivo, "w", encoding="utf-8") as página:
        página.write("""
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<title>Filmes</title>
</head>
<body>
        """)
        for c,v in filmes.items():
            página.write(f"<h1>{c.capitalize()}</h1>\n")
            for e in v:
                página.write(f"<{elemento}>{e}</{elemento}>\n")
        página.write("""
</body>
</html>
        """)
        
In [23]:
filmes_html(filmes, "filmes-p.html", elemento="p")
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<title>Filmes</title>
</head>
<body>
        <h1>Drama</h1>
<p>O Homem Elefante</p>
<p>O Poderoso Chefão</p>
<h1>Comédia</h1>
<p>Se beber não case</p>
<p>O Auto da Compadecida</p>
<p>American Pie</p>
<h1>Policial</h1>
<p>Seven</p>
<p>O silêncio dos inocentes</p>
<p>Os Homens que não Amavam as Mulheres</p>
<h1>Guerra</h1>
<p>Stalingrado</p>
<p>O Resgate do Soldado Ryan</p>
<p>Dunquerque</p>
<h1>Ficção científica</h1>
<p>O Expresso do Amanhã</p>
<p>Duna</p>
<p>Guerra nas Estrelas</p>
<p>A Ira de Kahn</p>

</body>
</html>
In [22]:
filmes_html(filmes, "filmes-h2.html", elemento="h2")
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<title>Filmes</title>
</head>
<body>
        <h1>Drama</h1>
<h2>O Homem Elefante</h2>
<h2>O Poderoso Chefão</h2>
<h1>Comédia</h1>
<h2>Se beber não case</h2>
<h2>O Auto da Compadecida</h2>
<h2>American Pie</h2>
<h1>Policial</h1>
<h2>Seven</h2>
<h2>O silêncio dos inocentes</h2>
<h2>Os Homens que não Amavam as Mulheres</h2>
<h1>Guerra</h1>
<h2>Stalingrado</h2>
<h2>O Resgate do Soldado Ryan</h2>
<h2>Dunquerque</h2>
<h1>Ficção científica</h1>
<h2>O Expresso do Amanhã</h2>
<h2>Duna</h2>
<h2>Guerra nas Estrelas</h2>
<h2>A Ira de Kahn</h2>

</body>
</html>

Exercício 6

Modofique o programa 9.8 para gerar uma lista html usando os elementos ul e li. Todos os elementos da lista devem estar dentro do elemento ul e cada item dentro de um elemento li. Exemplo:

<ul><li>Item 1</li><li>Item 2</li><li>Item 3</li></ul>

In [28]:
def filmes_html_item(filmes, arquivo="filmes.html"):
    with open(arquivo, "w", encoding="utf-8") as página:
        página.write("""
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<title>Filmes</title>
</head>
<body>
        """)
        for c,v in filmes.items():
            página.write(f"<h1>{c.capitalize()}</h1>\n")
            página.write(f"<ul>")
            for e in v:
                página.write(f"<li>{e}</li>\n")
            página.write(f"</ul>")
        página.write("""
</body>
</html>
        """)
        
In [29]:
filmes_html_item(filmes, "filmes-li.html")
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="utf-8">
<title>Filmes</title>
</head>
<body>
        <h1>Drama</h1>
<ul><li>O Homem Elefante</li>
<li>O Poderoso Chefão</li>
</ul><h1>Comédia</h1>
<ul><li>Se beber não case</li>
<li>O Auto da Compadecida</li>
<li>American Pie</li>
</ul><h1>Policial</h1>
<ul><li>Seven</li>
<li>O silêncio dos inocentes</li>
<li>Os Homens que não Amavam as Mulheres</li>
</ul><h1>Guerra</h1>
<ul><li>Stalingrado</li>
<li>O Resgate do Soldado Ryan</li>
<li>Dunquerque</li>
</ul><h1>Ficção científica</h1>
<ul><li>O Expresso do Amanhã</li>
<li>Duna</li>
<li>Guerra nas Estrelas</li>
<li>A Ira de Kahn</li>
</ul>
</body>
</html>
In [ ]:
 

Comentários

Comments powered by Disqus